ÿþ''*************************************** ''* Graphics Demo * ''* Author: Chip Gracey * ''* Copyright (c) 2005 Parallax, Inc. * ''* See end of file for terms of use. * ''*************************************** ''*Color / Screen layout commented version by Potatohead '2010 done at UPEW!! ''* ---> Enjoy!!! and share with your friends --a newbie will thank you! CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 {HYBRID example clock: _clkmode = xtal1 + pll16x 'Set these for your board _xinfreq = 6_000_000 } 'This allocates two complete bitmap screen buffers, consuming nearly all the HUB memory 'Each graphics "page" is $3000 in size, and one is the display page, the other being the draw 'page. By default, this program copies the draw page to the display page, after each 'iteration of the animated objects 'Considerable memory can be saved, by only having one page, with it being the draw and display 'page at the same time. This is known as a single buffer display, as opposed to the default 'double buffer one. Care must be taken when drawing objects to avoid flicker... 'It's also possible to draw in a small region of memory, and have it "pop" on to the display 'Region as well. _stack = ($3000 + $3000 + 100) >> 2 'accomodate display memory and stack x_tiles = 16 '16 pixels * 16 tiles = 256 pixel resolution in the X direction y_tiles = 12 '16 pixels * 12 tiles = 192 pixel resolution in the Y direction paramcount = 14 'This is the number of parameters passed to the TV driver bitmap_base = $2000 'This is the base address of the drawing page display_base = $5000 'The address of the display page. Make these two equal for single buffer lines = 5 'These two are just demo variables for the number of lines drawn thickness = 2 VAR 'Reserved variable space for comms with the mouse long mousex, mousey 'Reserved variable space for comms with the TV driver long tv_status '0/1/2 = off/visible/invisible read-only long tv_enable '0/? = off/on write-only long tv_pins '%ppmmm = pins write-only long tv_mode '%ccinp = chroma,interlace,ntsc/pal,swap write-only long tv_screen 'pointer to screen (words) write-only long tv_colors 'pointer to colors (longs) write-only long tv_hc 'horizontal cells write-only long tv_vc 'vertical cells write-only long tv_hx 'horizontal cell expansion write-only long tv_vx 'vertical cell expansion write-only long tv_ho 'horizontal offset write-only long tv_vo 'vertical offset write-only long tv_broadcast 'broadcast frequency (Hz) write-only long tv_auralcog 'aural fm cog write-only 'This is "the screen array" where all the pointers to form a bitmap are stored for the TV driver to use word screen[x_tiles * y_tiles] 'In the original program, the colors array was defined here, then computationally filled with values 'I've put it into the DAT section for clarity on how the colors really work. 'long colors[64] byte x[lines] 'Demo variable space for the spiraling lines byte y[lines] byte xs[lines] byte ys[lines] OBJ tv : "tv" 'Draws the actual TV graphics, based on parameters, colors array, screen array, HUB memory data gr : "graphics" 'Responsible for offering simple graphics primitive commands, line, arc, etc.... mouse : "mouse" 'Reads the mouse and keeps the position vars updated PUB start | i, j, k, kk, dx, dy, pp, pq, rr, numx, numchr, addr 'start tv 'Here the values defining how the TV driver is to operate are copied into it's HUB variable space 'allocated above longmove(@tv_status, @tvparams, paramcount) 'Here, the TV driver is given it's list of tile pixel data addresses tv_screen := @screen 'And now the TV driver is given the sets of colors to use with those addresses, forming "a tile" tv_colors := @colors 'Having set it all up, it's time to start the TV driver up, so we can see the graphics! tv.start(@tv_status) 'init tile screen "The Screen Array" { This bit of code, writes the hub screen memory address pointers for each tile on the screen. Each tile = 64 bytes, or 16 longs. Tiles are 16 pixels, 2 bits per pixel for a total of 1 long wide, and 16 pixels high. Tiles are numbered (0,0) at the upper right, incrementing horizontally, then down to reach the limits of x_tiles and y tiles as given above. The lower 10 bits of each screen address entry contain the address where the pixels for that tile are contained in the HUB. The upper 6 bits of each screen address entry contain the pointer to the color palette for that tile. Again, these lists are used by the TV driver to know where in the hub the picture is, and what colors it has. } repeat dx from 0 to tv_hc - 1 repeat dy from 0 to tv_vc - 1 screen[dy * tv_hc + dx] := display_base >> 6 + dy + dx * tv_vc '+ ((dy & $3F) << 10) 'I've commented out the original Parallax color scheme, which means all tiles are referencing 'Color Palette entry 0, which is black, dark green, light green background, and white, just to 'show how the tile color palettes work. 'Change the line below to whatever 4 colors you want, and those will be used for all but the two tiles 'I've pointed to the ROM below. Be careful to choose actual colors. 02 = black, and there is a color 'table in the graphics_palette.spin program to help you with this. Some color numbers actually equal 'signal values, and will mess up your display. Make one color change at a time, being careful to 'undo it, should you no longer see the images. set_color_palette(0, $02_07_7b_7d) 'Define color palette 0 '---------------------------------------------------------------------------------------------- ' The Parallax Demo stuff... '---------------------------------------------------------------------------------------------- 'init bouncing lines i := 1001 j := 123123 k := 8776434 repeat i from 0 to lines - 1 x[i] := ?j // 64 y[i] := k? // 48 repeat until xs[i] := k? ~> 29 repeat until ys[i] := ?j ~> 29 '----------------------------------------------------------------------------------------------- 'start and setup graphics 'We have to tell the graphics COG, where in the HUB to draw, and what the size of the screen is. 'The screen is 16 tiles in the X = 256 pixels, 12 tiles in the Y = 192 pixels. '128, and 96 are the X and Y "center pixel" values, which define the screen origin 'bitmap_base, is the start of the screen, or as defined above, the "draw page" 'Note: these can be changed on the fly, should you want to play tricks with the screen layout gr.start gr.setup(16, 12, 128, 96, bitmap_base) 'start mouse --> easy! 'nuff said. mouse.start(24, 25) '-------------------------------------------------------------------------------------------- { Here, I've called a few methods to make things really easy. These are to demonstrate how the tile address and color palette can be changed! I'll comment them in-line below, but for now, know the "set_tile_color" points the tile specified in terms of it's X and Y screen location (0,0) = upper left, to the colors DAT entry below, thus assigning the 4 colors contained in the colors entry to that particular tile. Each tile can have it's own 4 colors! For demo purposes, I've pointed two of the graphics tiles at the Propeller ROM, to display a character, instead of the pixels that exist in the HUB. The graphics COG does not know this, and is happily drawing pixels there anyway :) I've also set a few of the tiles to distinctive color combinations so that they can be seen and changed as needed more easily than has been possible in the past. See the method comments below... } set_tile_color(10,0,1) 'Set tile 10, 0 to palette 1 set_tile_color(10,1,1) 'Set tile 10, 1 to palette 1 set_tile_address(10,0,$8000+(128*47)) 'Point tile 10, 0 to ROM set_tile_address(10,1,$8000+(128*47)+64) 'Point tile 10, 1 to ROM '------------------------------------------------------------------------------------- repeat 200 'do the graphics demo 2000 iterations, or frames. 'clear bitmap gr.clear 'draw spinning triangles gr.colorwidth(3,0) repeat i from 1 to 8 gr.vec(0, 0, (k & $7F) << 3 + i << 5, k << 6 + i << 8, @vecdef) 'draw expanding mouse crosshairs gr.colorwidth(2,k>>2) mousex := mousex + mouse.delta_x #> -128 <# 127 mousey := mousey + mouse.delta_y #> -96 <# 95 gr.pix(mousex, mousey, k>>4 & $7, @pixdef) 'if left mouse button pressed, throw snowballs if mouse.button(0) gr.width(pq & $F) gr.color(2) pp := (pq & $F)*(pq & $F) + 5 pq++ gr.arc(mousex, mousey, pp, pp>>1, -k * 200, $200, 8, 0) else pq~ 'if right mouse button pressed, pause repeat while mouse.button(1) 'draw expanding pixel halo gr.colorwidth(1,k) gr.arc(0,0,80,30,-k<<5,$2000/9,9,0) 'step bouncing lines repeat i from 0 to lines - 1 if ||~x[i] > 60 -xs[i] if ||~y[i] > 40 -ys[i] x[i] += xs[i] y[i] += ys[i] 'draw bouncing lines gr.colorwidth(1,thickness) gr.plot(~x[0], ~y[0]) repeat i from 1 to lines - 1 gr.line(~x[i],~y[i]) gr.line(~x[0], ~y[0]) 'draw spinning stars and revolving crosshairs and dogs gr.colorwidth(2,0) repeat i from 0 to 7 gr.vecarc(80,50,30,30,-(i<<10+k<<6),$40,-(k<<7),@vecdef2) gr.pixarc(-80,-40,30,30,i<<10+k<<6,0,@pixdef2) gr.pixarc(-80,-40,20,20,-(i<<10+k<<6),0,@pixdef) 'draw small box with text gr.colorwidth(1,14) gr.box(60,-80,60,16) gr.textmode(1,1,6,5) gr.colorwidth(2,0) gr.text(90,-72,@pchip) 'draw incrementing digit if not ++numx & 7 numchr++ if numchr < "0" or numchr > "9" numchr := "0" gr.textmode(8,8,6,5) gr.colorwidth(1,8) gr.text(-90,50,@numchr) 'copy bitmap to display gr.copy(display_base) 'increment counter that makes everything change k++ '------------------------------------------------------------------------- 'Once the demo is over, make some screen changes ' 'Then stop! '------------------------------------------------------------------------- 'set a few tiles to different colors repeat dx from 2 to 7 repeat dy from 2 to 6 waitcnt (cnt + 10_000_000) set_tile_color(dx, dy, 2) 'Cycle one of the displayed colors repeat 5 dx := $1b_3c_9d_02 repeat k from $2b to $cb step 16 dx := dx & $FF_FF_00_FF dx := dx + K << 8 waitcnt (cnt + 10_000_000) set_color_palette(2, dx) 'Set that region to a different background color dx := dx & $FF_FF_FF_00 dx := dx + $2D set_color_palette(2, dx) 'Clone part of the screen by changing tile addresses 'And assign it it's own palette repeat dx from 2 to 7 repeat dy from 2 to 6 addr := get_tile_address(dx, dy) set_tile_address(dx + 8, dy + 3, addr) set_tile_color(dx + 8, dy +3, 5) waitcnt (cnt + 10_000_000) waitcnt (cnt + 100_000_000) 'Show that it's all still in the HUB! 'Using the original code that defined the screen addresses to draw in repeat dx from 0 to tv_hc - 1 repeat dy from 0 to tv_vc - 1 waitcnt(cnt + 5_000_000) screen[dy * tv_hc + dx] := display_base >> 6 + dy + dx * tv_vc set_tile_color(dx, dy, 5) 'set them to monochrome 'Now it's possible to redefine the bitmap base, allowing for graphics "windows" 'and other tricks. What needs to happen is the tiles must be pointed to a 'sequental set of addresses, then that base address and tile dimenions gets 'communicated to the graphics cog via the method "gr.setup" 'Define a new bitmap space and coordinates that draw into the cloned portion of the screen 'New screen is 6 tiles = 96 pixels in the X, and 5 tiles = 80 in the Y 'Screen center is = 0, 0 for a standard upper left coordinate system, instead of the one used 'by the demo code above. 'Once this is done, the graphics draw commands will operate on the new screen dimensions 'To draw on the full screen, gr.setup must be run again, with the original specifications 'For this demo, part of the draw screen will be used, so our screen addresses will start at $2000 'Screen addresses are sequential, 64 byte chunks, running from upper left, horizontally to the right 'then filling down, ending at lower left. Each tile is 64 bytes. ' Tile 0,0 = $2000 Tile 1,0 = $2040 Tile 2,0 = $2080 Tile 3,0 = $20C0 ... Tile 5,0 = $2140 ' Tile 0,1 = $2180 'First point the tiles to the right screen addresses, offset into screen in both directions to make 'the new graphics area like a "window" addr := $2000 repeat dx from 0 to 5 '6 tiles in the X = 96 pixels repeat dy from 0 to 4 '5 tiles in the Y = 80 pixels set_tile_address(dx + 3, dy + 3, addr) set_tile_color(dx + 3, dy + 3, 2) addr := addr + 64 gr.setup(6, 5, 0, 0, $2000) 'Time time, we make 0,0 the bottom left corner, instead of the center waitcnt (cnt + 50_000_000) 'Pause to see the left over graphics in the draw page from the demo gr.clear 'Clear the screen 'draw some lines that radiate out from 0,0 to the extents of the screen repeat dx from 0 to 95 step 3 gr.colorwidth(3,0) gr.plot(0,0) gr.line(dx, 79) repeat dy from 0 to 79 step 5 gr.colorwidth(2,1) gr.plot(95,0) gr.line(0, dy) waitcnt(cnt + 50_000_000) 'Because we drew the bitmap in the double buffer draw space, the original screen is still in the HUB! repeat dx from 0 to tv_hc - 1 repeat dy from 0 to tv_vc - 1 waitcnt(cnt + 5_000_000) screen[dy * tv_hc + dx] := display_base >> 6 + dy + dx * tv_vc set_tile_color(dx, dy, 2) 'Restore original graphics screen 'And end with the lines we drew, defined as a window somewhere else!! addr := $2000 repeat dx from 0 to 5 '6 tiles in the X = 96 pixels repeat dy from 0 to 4 '5 tiles in the Y = 80 pixels set_tile_address(dx + 6, dy + 4, addr) set_tile_color(dx + 6, dy + 4, 7) addr := addr + 64 PUB set_tile_color(tilex, tiley, palette) | temp 'doug@opengeek.org 'The screen[] array contains both the tile HUB image data address, and an index into the color palette 'array, which can be defined as a DAT block, as done in this program, or as a VAR as done in the 'original 'The lower 10 bits are the HUB image address data (where the pixels are defined) 'The upper 6 bits point to sets of colors, contained in the colors[] array temp := screen[tiley * tv_hc + tilex] 'get current screen tile value temp := temp & %00000011_11111111 'clear color palette index value, leaving image address bits temp := temp + (palette << 10) 'replace color palette index value with the new one screen[tiley * tv_hc + tilex] := temp 'update the screen PUB set_color_palette(palette_num, palette_colors) 'doug@opengeek.org 'only 64 palettes possible... (& %111111) insures no overwrite occurs on larger values 'the colors run like this in the long: 33_22_11_00, where 33 = color 3, 22 = color 2, etc... 'color 00 is the background color. colors[palette_num & %111111] := palette_colors 'Write the colors into the colors[] array PUB set_tile_address(tilex, tiley, HUB_address) | temp 'doug@opengeek.org temp := screen[tiley * tv_hc + tilex] 'Get current screen tile value from screen[] array temp := temp & %11111100_00000000 'Zero out the screen memory address pointer bits, leaving color bits temp := temp + (HUB_address >> 6) 'Set screen memory pointer bits, from the most significant bits in the HUB 'address given. Tiles must be 64 byte aligned, due to how the TV driver 'decodes the screen array bits, 6 for color, 10 for screen pixel address in HUB screen[tiley * tv_hc + tilex] := temp 'Update screen array for that tile, reflecting address change. PUB get_tile_address(tilex, tiley) | temp 'doug@opengeek.org temp := screen[tiley * tv_hc + tilex] 'Get current screen tile value from screen[] array temp := temp & %00000011_11111111 'Zero out the color bits, leaving just the address temp := temp << 6 'Shift to reflect effective HUB address. Tiles must 'be 64 byte aligned, due to how the TV driver 'decodes the screen array bits, 6 for color, 10 for 'screen pixel address in HUB return temp 'return that address! DAT 'This is where we define the TV parameters to be used for our graphics show 'The documentation for these is actually in the TV driver, so go look there 'for Chip's rather complete explanation of what all these do. ' 'All you really need to do, in order to make this work on your board, is deal 'with the right clock setting at the program top, set your pin group according 'to the documentation in the TV driver, and the display mode bits, again according 'to the TV driver comments, and your hardware setup. 'I've included a sample change for the HYBRID 96Mhz board, as an example. { 'HYBRID tvparams long 0 'status long 1 'enable long %011_0000 'pins *** long %0000 'mode *** long 0 'screen long 0 'colors long x_tiles 'hc long y_tiles 'vc long 10 'hx long 1 'vx long 0 'ho long 0 'vo long 0 'broadcast long 0 'auralcog } 'Demoboard defaults... tvparams long 0 'status long 1 'enable long %001_0101 'pins long %0000 'mode long 0 'screen long 0 'colors long x_tiles 'hc long y_tiles 'vc long 10 'hx long 1 'vx long 0 'ho long 0 'vo long 0 'broadcast long 0 'auralcog DAT { I've put the colors array here in the DAT section to illustrate how the colors actually work. Feel free to define a smaller array in a VAR section, or cut this one down to the number of unique color combinations you think you need. The only ones necessary, are those you define as in use with the SPIN methods: set_tile_color(tilex, tiley, palette) ==> Used to assign a palette to a screen tile set_color_palette(palette_num, palette_colors) ==> Used to change the contents of a palette } '64 Palette entries follow! colors long $07_05_04_02 'This is palette 0 long $02_04_06_07 'This is palette 1 long $1b_3c_9d_02 'This is palette 2, containing a few colors long $07_05_04_02 long $07_05_04_02 'All of them are greyscale, with black background to start long $07_05_04_02 'And some are changed by the color demo above... long $07_05_04_02 long $1b_3c_9d_02 'A lot of them go unused, but are here for clarity long $07_05_04_02 long $07_05_04_02 'When you get it all sorted, feel free to trim the fat! long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 long $07_05_04_02 DAT 'These are things used in the little Parallax demo. vecdef word $4000+$2000/3*0 'triangle word 50 word $8000+$2000/3*1+1 word 50 word $8000+$2000/3*2-1 word 50 word $8000+$2000/3*0 word 50 word 0 vecdef2 word $4000+$2000/12*0 'star word 50 word $8000+$2000/12*1 word 20 word $8000+$2000/12*2 word 50 word $8000+$2000/12*3 word 20 word $8000+$2000/12*4 word 50 word $8000+$2000/12*5 word 20 word $8000+$2000/12*6 word 50 word $8000+$2000/12*7 word 20 word $8000+$2000/12*8 word 50 word $8000+$2000/12*9 word 20 word $8000+$2000/12*10 word 50 word $8000+$2000/12*11 word 20 word $8000+$2000/12*0 word 50 word 0 pixdef word 'crosshair byte 2,7,3,3 word %%00333000,%%00000000 word %%03020300,%%00000000 word %%30020030,%%00000000 word %%32222230,%%00000000 word %%30020030,%%02000000 word %%03020300,%%22200000 word %%00333000,%%02000000 pixdef2 word 'dog byte 1,4,0,3 word %%20000022 word %%02222222 word %%02222200 word %%02000200 pchip byte "Propeller",0 'text {{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % TERMS OF USE: MIT 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. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }}