diff --git a/games/starblaster/.gitignore b/games/starblaster/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4a0248f59a429520f564b6c6c5432b4fb5248599 --- /dev/null +++ b/games/starblaster/.gitignore @@ -0,0 +1 @@ +/sdbins diff --git a/games/starblaster/build.sh b/games/starblaster/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..8573204dc07c72f301dba3ff4ccbac780d69263d --- /dev/null +++ b/games/starblaster/build.sh @@ -0,0 +1,6 @@ +mkdir -p sdbins +cd source +rm ../sdbins/STBLST.BIN +bstc -e -o ../sdbins/STBLST Fk_starblaster_010.spin +mv ../sdbins/STBLST.eeprom ../sdbins/STBLST.BIN +cd .. \ No newline at end of file diff --git a/games/starblaster/original/87288.zip b/games/starblaster/original/87288.zip new file mode 100644 index 0000000000000000000000000000000000000000..2074da3e721d2532cf6d3b207f7f55a7c3fd440a Binary files /dev/null and b/games/starblaster/original/87288.zip differ diff --git a/games/starblaster/source/Fk_NESDriver_001.spin b/games/starblaster/source/Fk_NESDriver_001.spin new file mode 100644 index 0000000000000000000000000000000000000000..cf481cce400e52a0e68d305b560cb345d76c83d0 --- /dev/null +++ b/games/starblaster/source/Fk_NESDriver_001.spin @@ -0,0 +1,68 @@ +' ///////////////////////////////////////////////////////////////////////////// +' NES Controller Driver +' AUTHOR: Frank Korf +' LAST MODIFIED: 1.29.09 +' VERSION 0.1 +' ///////////////////////////////////////////////////////////////////////////// +CON + + JOY_CLK = 16 + JOY_LCH = 17 + JOY_DATAOUT0 = 19 + JOY_DATAOUT1 = 18 + + ' NES bit encodings for NES gamepad 0 + NES0_RIGHT = %00000000_00000001 + NES0_LEFT = %00000000_00000010 + NES0_DOWN = %00000000_00000100 + NES0_UP = %00000000_00001000 + NES0_START = %00000000_00010000 + NES0_SELECT = %00000000_00100000 + NES0_B = %00000000_01000000 + NES0_A = %00000000_10000000 + +VAR + long nespad0 + +PUB Update + nespad0 := Read_Gamepad_0 + +PUB Up : nesbits + nesbits := ( (nespad0 & NES0_UP) <> 0 ) +PUB Down : nesbits + nesbits := ( (nespad0 & NES0_DOWN) <> 0 ) +PUB Left : nesbits + nesbits := ( (nespad0 & NES0_LEFT) <> 0 ) +PUB Right : nesbits + nesbits := ( (nespad0 & NES0_RIGHT) <> 0 ) +PUB Start : nesbits + nesbits := ( (nespad0 & NES0_START) <> 0 ) +PUB Select : nesbits + nesbits := ( (nespad0 & NES0_SELECT) <> 0 ) +PUB A : nesbits + nesbits := ( (nespad0 & NES0_A) <> 0 ) +PUB B : nesbits + nesbits := ( (nespad0 & NES0_B) <> 0 ) + +PUB Read_Gamepad_0 : nes_bits | i + DIRA [JOY_CLK] := 1 ' output + DIRA [JOY_LCH] := 1 ' output + DIRA [25] := 0 ' input + DIRA [26] := 0 ' input + + OUTA [JOY_CLK] := 0 ' JOY_CLK = 0 + OUTA [JOY_LCH] := 0 ' JOY_SH/LDn = 0 + OUTA [JOY_LCH] := 1 ' JOY_SH/LDn = 1 + OUTA [JOY_LCH] := 0 ' JOY_SH/LDn = 0 + nes_bits := 0 + nes_bits := INA[JOY_DATAOUT0] | (INA[JOY_DATAOUT1] << 8) + + repeat i from 0 to 6 + OUTA [JOY_CLK] := 1 ' JOY_CLK = 1 + OUTA [JOY_CLK] := 0 ' JOY_CLK = 0 + nes_bits := (nes_bits << 1) + nes_bits := nes_bits | INA[JOY_DATAOUT0] | (INA[JOY_DATAOUT1] << 8) + + nes_bits := (!nes_bits & $FFFF) + +DAT \ No newline at end of file diff --git a/games/starblaster/source/Fk_graphics_drv_010.spin b/games/starblaster/source/Fk_graphics_drv_010.spin new file mode 100644 index 0000000000000000000000000000000000000000..802d44ff3e06ed54f068c1420a1e2fe14576519d Binary files /dev/null and b/games/starblaster/source/Fk_graphics_drv_010.spin differ diff --git a/games/starblaster/source/Fk_starblaster_010.spin b/games/starblaster/source/Fk_starblaster_010.spin new file mode 100644 index 0000000000000000000000000000000000000000..4d8d3eccc99e095275871bb1e014b24faabbc9d3 --- /dev/null +++ b/games/starblaster/source/Fk_starblaster_010.spin @@ -0,0 +1,675 @@ +' ///////////////////////////////////////////////////////////////////////////// +' StarBlaster Game +' AUTHOR: Frank Korf +' LAST MODIFIED: 1.29.12 +' VERSION 1.0 +' COMMENTS: Simple space shooter +' Special thanks to Andre' LaMothe and the Hydra team +' +' CONTROLS: gamepad (must be plugged in) +' Start = "Start" +' Move = "Dpad" +' Shoot = "A" +' ///////////////////////////////////////////////////////////////////////////// + +'/////////////////////////////////////////////////////////////////////// +' CONSTANTS SECTION //////////////////////////////////////////////////// +'/////////////////////////////////////////////////////////////////////// + +CON + + _clkmode = xtal1 + pll16x ' enable external clock and pll times 8 + _xinfreq = 5_000_000 ' set frequency to 10 MHZ plus some error + _stack = ($3000 + $3000 + 64) >> 2 'accomodate display memory and stack + + ' graphics driver and screen constants + PARAMCOUNT = 14 + OFFSCREEN_BUFFER = $2000 ' offscreen buffer + ONSCREEN_BUFFER = $5000 ' onscreen buffer + + ' size of graphics tile map + X_TILES = 16 + Y_TILES = 12 + + SCREEN_WIDTH = 256 + SCREEN_HEIGHT = 192 + + ' text position constants + SCORE_X_POS = -SCREEN_WIDTH/2 + 10 + SCORE_Y_POS = SCREEN_HEIGHT/2 - 1*14 + + HISCORE_X_POS = -24 + HISCORE_Y_POS = SCREEN_HEIGHT/2 - 1*14 + + SHIPS_X_POS = SCREEN_WIDTH/2 - 10/2*12 + SHIPS_Y_POS = SCREEN_HEIGHT/2 - 1*14 + + ' angular constants to make object declarations easier + ANG_0 = $0000 + ANG_360 = $2000 + ANG_240 = ($2000*2/3) + ANG_180 = ($2000/2) + ANG_120 = ($2000/3) + ANG_90 = ($2000/4) + ANG_60 = ($2000/6) + ANG_45 = ($2000/8) + ANG_30 = ($2000/12) + ANG_22_5 = ($2000/16) + ANG_15 = ($2000/24) + ANG_10 = ($2000/36) + ANG_5 = ($2000/72) + + ' game states + GAME_STATE_INIT = 0 + GAME_STATE_MENU = 1 + GAME_STATE_START = 2 + GAME_STATE_RUN = 3 + GAME_STATE_OVER = 4 + + 'Object Contstants + OBJECT_SIZE = 3 + _POSITION 'long + _X = $0 'word + _Y = $1 'word + _VEL = $2 'word + _VEL_X = $4 'byte + _VEL_Y = $5 'byte + _LIFETIME = $3 'word + + 'Player Constants + PLAYER_SIZE = OBJECT_SIZE + PLAYER_SPEED = 3 + RELOAD_TIME = 15 + + 'Bullet Constants + MAX_BULLETS = 3 + BULLET_SIZE = OBJECT_SIZE + 1 + BULLET_SPEED = 4 + BULLET_LIFETIME = 30 + + 'Mob Constants + MOB_SIZE = OBJECT_SIZE + 2 + MAX_MOBS = 8 + _AI = $4 + _AI_COUNT = $8 + _AI_STEP = $9 + + 'Explosion Constants + _EXPLOSION_LIFE = $2 + + +'/////////////////////////////////////////////////////////////////////// +' VARIABLES SECTION //////////////////////////////////////////////////// +'/////////////////////////////////////////////////////////////////////// + +VAR + 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 + + word screen[X_TILES * Y_TILES] ' storage for screen tile map + long colors[64] ' color look up table + + word map_height[64] + word seed + + byte game_state + byte restart_timer + + word score + word high_score + + 'two words per star: x and y pos + word star_list[64] + + word camera_x + word camera_y + + 'four words for the character + word player[PLAYER_SIZE] + word num_ships + + 'four words per bullet: x and y pos, direction, lifetime + word bullet_list[ MAX_BULLETS * BULLET_SIZE ] + word num_bullets + word bullet_timer + + 'four words per mob: x and y pos, direction, ai state + word mob_list[ MAX_MOBS * MOB_SIZE ] + word num_mobs + word mob_timer + + ' + word explosion_list[ MAX_MOBS * OBJECT_SIZE ] + + +'/////////////////////////////////////////////////////////////////////// +' OBJECT DECLARATION SECTION /////////////////////////////////////////// +'/////////////////////////////////////////////////////////////////////// +OBJ + tv : "tv.spin" ' instantiate a tv object + gr : "Fk_graphics_drv_010.spin" ' instantiate a graphics object + nes : "Fk_NESDriver_001.spin" +'/////////////////////////////////////////////////////////////////////// +' PUBLIC FUNCTIONS ///////////////////////////////////////////////////// +'/////////////////////////////////////////////////////////////////////// + +PUB start | i, dx, dy + + game_state := GAME_STATE_INIT + + 'start tv + longmove(@tv_status, @tvparams, paramcount) + tv_screen := @screen + tv_colors := @colors + tv.start(@tv_status) + + 'init colors + repeat i from 0 to 64 + colors[i] := $00001010 * (i+4) & $F + $FB060C02 + + 'init tile screen + repeat dx from 0 to tv_hc - 1 + repeat dy from 0 to tv_vc - 1 + screen[dy * tv_hc + dx] := onscreen_buffer >> 6 + dy + dx * tv_vc + ((dy & $3F) << 10) + + 'start and setup graphics 256x192, with orgin (0,0) at bottom left of screen + gr.start + gr.setup(X_TILES, Y_TILES, SCREEN_WIDTH/2, SCREEN_HEIGHT/2, offscreen_buffer) + + ' BEGIN GAME LOOP //////////////////////////////////////////////////// + + ' infinite loop + repeat while TRUE + case game_state + GAME_STATE_INIT: + GameInit + game_state := GAME_STATE_START + GAME_STATE_START: + GameStart + game_state := GAME_STATE_MENU + i := 0 + GAME_STATE_RUN,GAME_STATE_MENU,GAME_STATE_OVER: + + 'clear the offscreen buffer + gr.clear + + camera_x++ + nes.Update + if( game_state == GAME_STATE_RUN ) + GameUpdate + else + if( nes.Start ) + game_state := GAME_STATE_RUN + i := 0 + + ' RENDERING SECTION (render to offscreen buffer always////////////// + + RenderMap(camera_x,camera_y) + + if( game_state == GAME_STATE_RUN ) + GameRender + else + ' draw start game text + gr.textmode(3,1,6,5) + gr.colorwidth(2,0) + if (i > 200) + if (++i & $8) + gr.text(0,0,@start_string) + else + i++ + if ( game_state == GAME_STATE_MENU ) + gr.text(0,0,@title_string) + else + gr.text(0,0,@over_string) + + HudRender + + 'copy bitmap to display offscreen -> onscreen + gr.copy(onscreen_buffer) + + ' synchronize to frame rate would go here... + + ' END RENDERING SECTION /////////////////////////////////////////////// + + ' END MAIN GAME LOOP REPEAT BLOCK ////////////////////////////////// + +'//////Game Functions + +PUB GameInit + restart_timer := $FF + seed := 1234 + high_score := 0 + +PUB GameStart + camera_x := 80 + camera_y := 90 + PrepPlayer + PrepMap + PrepBullets + PrepMobs + PrepExplosions + +PUB GameUpdate + UpdatePlayer + UpdateBullets + UpdateMobs + UpdateExplosions + ProcessCollisions + +PUB GameRender + RenderBullets(camera_x,camera_y) + RenderMobs(camera_x,camera_y) + RenderExplosions( camera_x, camera_y ) + + if( restart_timer == $FF ) + gr.colorwidth(2, 0) + gr.pix( player.WORD[_X] - camera_x, player.WORD[_Y] - camera_y, 0, @pix_player ) + + +PUB HudRender | i + gr.colorwidth(2, 0) + ' draw score and num players + gr.textmode( 2,1,6,0 ) + gr.text(SCORE_X_POS, SCORE_Y_POS, @score_string) + gr.text(HISCORE_X_POS, HISCORE_Y_POS, @hiscore_string) + gr.text(SHIPS_X_POS, SHIPS_Y_POS, @ships_string) + + PrintScore( score, SCORE_X_POS + 10, SCORE_Y_POS - 13 ) + PrintScore( high_score, HISCORE_X_POS + 5, HISCORE_Y_POS - 13 ) + + ' draw players ships left + gr.colorwidth(3,0) + repeat i from 0 to num_ships + gr.pix( SHIPS_X_POS + i << 4 + 12, SHIPS_Y_POS - 10, 1, @pix_player ) + + +PUB PrintScore( i, x, y ) | t, str + str := 5 + gr.colorwidth(2,0) + gr.textmode( 1,1,6,0 ) + repeat t from 0 to 6 + BYTE[ @score_number + str ] := 48 + (i // 10) + i /= 10 + str-- + gr.text( x,y, @score_number ) + + + +'////Player Code +PUB PrepPlayer + player.WORD[_X] := 0 + player.WORD[_Y] := 100 + player.WORD[_VEL] := $00_00 + num_ships := 2 + score := 0 + +PUB UpdatePlayer + +if( restart_timer == $FF ) + player.WORD[_VEL] := $00_00 + if( nes.Right ) + player.BYTE[_VEL_X] := 2 + if( nes.Left ) + player.BYTE[_VEL_X] := -1 + if( nes.Up ) + player.BYTE[_VEL_Y] := 2 + if( nes.Down ) + player.BYTE[_VEL_Y] := -2 + if( nes.A ) + AddBullet( player.WORD[_X], player.WORD[_Y] ) + + player.WORD[_X]++ + if( player.WORD[_X] > camera_x ) + player.WORD[_X] := camera_x + if( camera_x - player.WORD[_X] > 100 ) + player.WORD[_X] := camera_x - 100 + if( player.WORD[_Y] < 20 ) + player.WORD[_Y] := 20 + if( player.WORD[_Y] > 180 ) + player.WORD[_Y] := 180 + + UpdatePos( @player ) +elseif( restart_timer == 0 ) + ' if we're out of ships, game over + restart_timer := $FF + if( num_ships == 0 ) + game_state := GAME_STATE_OVER + if( score > high_score ) + high_score := score + ' else replace the player and reset the timer + GameStart + else + num_ships-- + 'subtract a ship +else + restart_timer -- + +'/////TERRAIN AND STAR CODE + +PUB PrepMap | i , s + s := 1234 + i := 0 + repeat 64 + map_height[i] := ?s + map_height[i] //= 20 + i++ + i := 0 + repeat 66 + map_height[i//64] := ( map_height[ i//64 ] + map_height[ (i+1)//64 ] + map_height[ (i+2)//64 ] ) / 3 + i++ + + i := 0 + repeat 32 + star_list[i] := ?s + star_list[i] //= 360 + star_list[i+1] := ?s + star_list[i+1] //= 180 + i += 2 + +PUB RenderMap(x,y) | i, j, first + gr.colorwidth(2,1) + i := 0 + j := ( x / 10 ) // 64 + first := x // 10 + first += 130 + gr.plot( -first, -y + map_height[j] ) + repeat 28 + gr.line( -first + i*10, -y + map_height[j] ) + i++ + j := (j+1) // 64 + + i := 0 + gr.colorwidth(2, 0) + repeat 32 + gr.plot( star_list[i] - 180, star_list[i+1] - 90 ) + i += 2 + + +'/////////BULLET CODE +PUB PrepBullets | i + i := 0 + REPEAT MAX_BULLETS * BULLET_SIZE + bullet_list[i] := $0000 + i++ + num_bullets := 0 + bullet_timer := 1 + +PUB AddBullet( x, y ) | i + i := 0 + if( num_bullets < MAX_BULLETS ) + if( bullet_timer == 0 ) + REPEAT UNTIL bullet_list[i]==0 + i+=BULLET_SIZE + bullet_list.WORD[i+_X] := x + bullet_list.WORD[i+_Y] := y + bullet_list.WORD[i+_LIFETIME] := BULLET_LIFETIME + bullet_timer := RELOAD_TIME + +PUB RenderBullets( x, y ) | i + gr.colorwidth(2, 1) + i := 0 + repeat MAX_BULLETS + if( bullet_list[i] <> 0 ) + gr.plot( bullet_list.WORD[i+_X] - x, bullet_list.WORD[i+_Y] - y) + i += BULLET_SIZE + +PUB UpdateBullets | i +i := 0 +num_bullets := 0 +repeat MAX_BULLETS + if( bullet_list[i] <> 0 ) + bullet_list.WORD[i+_X] += BULLET_SPEED + bullet_list.WORD[i+_LIFETIME]-- + if( bullet_list.WORD[i+_LIFETIME] == 0 ) + bullet_list[i] := 0 + else + num_bullets++ + i += BULLET_SIZE +if( bullet_timer > 0 ) + bullet_timer-- + + +'/////////MOB CODE +PUB PrepMobs | i + i := 0 + REPEAT MOB_SIZE * MAX_MOBS + mob_list[i] := $00_00 + i++ + mob_timer := 10 + num_mobs := 0 + +PUB UpdateMobs | i + i := 0 + num_mobs := 0 + + repeat MAX_MOBS + if( mob_list[i] <> 0 ) + UpdateAI( @mob_list[i] ) + UpdatePos( @mob_list[i] ) + mob_list.WORD[i+_LIFETIME]-- + if( mob_list.WORD[i+_LIFETIME] == 0 ) + mob_list[i] := 0 + else + num_mobs++ + i += MOB_SIZE + + mob_timer-- + if( mob_timer == 0 ) + mob_timer := 30 + if( num_mobs < MAX_MOBS ) + AddMob + +PUB AddMob | i + i:=0 + REPEAT UNTIL mob_list[i] == 0 + i += MOB_SIZE + mob_list.WORD[i+_X] := camera_x + 200 + mob_list.WORD[i+_Y] := ?seed + mob_list.WORD[i+_Y] //= 120 + mob_list.WORD[i+_Y] += 40 + mob_list.WORD[i+_VEL] := WORD[ @mob_movement[0] ] 'vel + mob_list.WORD[i+_LIFETIME] := 500 'lifetime + mob_list.WORD[i+_AI] := $00_00 'ai state + + +PUB UpdateAI( mob_addr ) | count, curr_step + count := BYTE[mob_addr][_AI_COUNT] + curr_step := BYTE[mob_addr][_AI_STEP] + count ++ + if( count > 10 ) + count := 0 + curr_step := (curr_step+1) // 6 + WORD[ mob_addr ][ _VEL ] := WORD[ @mob_movement[ curr_step ] ] + BYTE[ mob_addr ][ _AI_STEP ] := curr_step + + BYTE[ mob_addr ][ _AI_COUNT ] := count + + +PUB RenderMobs( x, y ) | i + gr.colorwidth(3, 0) + i := 0 + repeat MAX_MOBS + if( mob_list[i] <> 0 ) + gr.pix( mob_list.WORD[i+_X] - x, mob_list.WORD[i+_Y]-y, 4, @pix_tie ) + i += MOB_SIZE + +'//////Explosion Code +PUB PrepExplosions | i + i := 0 + REPEAT OBJECT_SIZE * MAX_MOBS + explosion_list[i] := $0 + i++ + +PUB UpdateExplosions | i + i := 0 + REPEAT MAX_MOBS + if( explosion_list[i] <> 0 ) + explosion_list[i+_EXPLOSION_LIFE]+=3 + if( explosion_list[i+_EXPLOSION_LIFE] > 30 ) + explosion_list[i] := 0 + i += OBJECT_SIZE + +PUB AddExplosion( x, y ) | i + i := 0 + REPEAT UNTIL explosion_list[i] == 0 + i += OBJECT_SIZE + explosion_list[i+_X] := x + explosion_list[i+_Y] := y + explosion_list[i+_EXPLOSION_LIFE] := 0 + + +PUB RenderExplosions(x, y) | i,offset + gr.colorwidth(1, 1) + i := 0 + REPEAT MAX_MOBS + if( explosion_list[i] <> 0 ) + offset := explosion_list[ i + _EXPLOSION_LIFE ] + gr.vecarc(explosion_list[i+_X]-x,explosion_list[i+_Y]-y,offset/5,offset/5,-(offset<<10+offset<<6),offset,-(offset),@vec_star) + i += OBJECT_SIZE + +'//////Object Physics + +PUB UpdatePos( obj_addr ) + WORD[obj_addr][_X] := (BYTE[obj_addr][_VEL_X]+128)//256 + WORD[obj_addr][_X] -128 + WORD[obj_addr][_Y] := (BYTE[obj_addr][_VEL_Y]+128)//256 + WORD[obj_addr][_Y] -128 + +'/////////////Collision Detection + +PUB ProcessCollisions | i,j,dx,dy,dist + i := 0 + repeat MAX_BULLETS + if ( bullet_list[i] <> 0 ) + j := 0 + repeat MAX_MOBS + if( mob_list[j] <> 0 ) + dx := bullet_list.WORD[i+_X] - mob_list.WORD[j+_X] + dy := bullet_list.WORD[i+_Y] - mob_list.WORD[j+_Y] + dist := (dx*dx+dy*dy) + if( dist < 220 ) + AddExplosion( bullet_list[i+_X], bullet_list[i+_Y] ) + bullet_list[i] := 0 + mob_list[j] := 0 + score ++ + j += MOB_SIZE + i += BULLET_SIZE + i := 0 + repeat MAX_MOBS + if( mob_list[i] <> 0 ) + dx := player.WORD[_X] - mob_list.WORD[i+_X] + dy := player.WORD[_Y] - mob_list.WORD[i+_Y] + dist := (dx*dx+dy*dy) + if( dist < 280 ) + restart_timer := 100 + AddExplosion( player[_X], player[_Y] ) + mob_list[i] := 0 + i += MOB_SIZE + +'/////////////////////////////////////////////////////////////////////// +' DATA SECTION ///////////////////////////////////////////////////////// +'/////////////////////////////////////////////////////////////////////// + +DAT + +' TV PARAMETERS FOR DRIVER ///////////////////////////////////////////// + +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 timing stretch + long 1 'vx + long 0 'ho + long 0 'vo + long 55_250_000 'broadcast + long 0 'auralcog + +score_number + 'text'123456' 6 bytes + byte "xxxxxx",0 + +score_string byte "Score",0 'text +hiscore_string byte "High",0 'text +ships_string byte "Ships",0 'text +start_string byte "PRESS START",0 'text +title_string byte "StarBlaster",0 'text +over_string byte "GAME OVER",0 + +mob_movement + word $01_00 + word $FE_00 + word $01_01 + word $FE_F0 + word $01_00 + word $FE_00 + +pix_dog word 'dog + byte 1,4,0,0 + word %%20000022 + word %%02222222 + word %%02222200 + word %%02000200 + +pix_tie word ' tie fighter in normal wing configuration + byte 2,8,3,3 ' 2 words wide (8 pixels) x 8 words high (8 lines), 8x8 sprite + word %%01000000,%%00000010 + word %%10000000,%%00000001 + word %%10000111,%%11100001 + word %%11111111,%%11111111 + word %%11111111,%%11111111 + word %%10000111,%%11100001 + word %%10000000,%%00000001 + word %%01000000,%%00000010 + +pix_player word + byte 1, 8, 0, 3 + word %%00011110 + word %%00110000 + word %%11111000 + word %%01111111 + word %%01111111 + word %%11111000 + word %%00110000 + word %%00011110 +vec_star 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 \ No newline at end of file diff --git a/games/starblaster/source/TV.spin b/games/starblaster/source/TV.spin new file mode 100644 index 0000000000000000000000000000000000000000..3bc856b593f7405fa5f602c3e9d2347a72811b28 --- /dev/null +++ b/games/starblaster/source/TV.spin @@ -0,0 +1 @@ +''***************************** ''* TV Driver v1.1 * ''* (C) 2004 Parallax, Inc. * ''***************************** ' v1.0 - 01 May 2006 - original version ' v1.1 - 17 May 2006 - pixel tile size can now be 16 x 32 to enable more efficient ' character displays utilizing the internal font - see 'tv_mode' CON fntsc = 3_579_545 'NTSC color frequency lntsc = 3640 'NTSC color cycles per line * 16 sntsc = 624 'NTSC color cycles per sync * 16 fpal = 4_433_618 'PAL color frequency lpal = 4540 'PAL color cycles per line * 16 spal = 848 'PAL color cycles per sync * 16 paramcount = 14 colortable = $180 'start of colortable inside cog VAR long cog PUB start(tvptr) : okay '' Start TV driver - starts a cog '' returns false if no cog available '' '' tvptr = pointer to TV parameters stop okay := cog := cognew(@entry, tvptr) + 1 PUB stop '' Stop TV driver - frees a cog if cog cogstop(cog~ - 1) DAT '******************************* '* Assembly language TV driver * '******************************* org ' ' ' Entry ' entry mov taskptr,#tasks 'reset tasks mov x,#10 'perform task sections initially :init jmpret taskret,taskptr djnz x,#:init ' ' ' Superfield ' superfield mov taskptr,#tasks 'reset tasks test _mode,#%0001 wc 'if ntsc, set phaseflip if_nc mov phaseflip,phasemask test _mode,#%0010 wz 'get interlace into nz ' ' ' Field ' field mov x,vinv 'do invisible back porch lines :black call #hsync 'do hsync waitvid burst,sync_high2 'do black jmpret taskret,taskptr 'call task section (z undisturbed) djnz x,#:black 'another black line? wrlong visible,par 'set status to visible mov x,vb 'do visible back porch lines call #blank_lines mov screen,_screen 'point to first tile (upper-leftmost) mov y,_vt 'set vertical tiles :line mov vx,_vx 'set vertical expand :vert if_z xor interlace,#1 'interlace skip? if_z tjz interlace,#:skip call #hsync 'do hsync mov vscl,hb 'do visible back porch pixels xor tile,colortable waitvid tile,#0 mov x,_ht 'set horizontal tiles mov vscl,hx 'set horizontal expand :tile rdword tile,screen 'read tile or tile,line 'set pointer bits into tile rol tile,#6 'read tile pixels rdlong pixels,tile '(2 instructions between reads) shr tile,#10+6 'set tile colors movs :color,tile add screen,#2 'point to next tile mov tile,phaseflip :color xor tile,colortable waitvid tile,pixels 'pass colors and pixels to video djnz x,#:tile 'another tile? sub screen,hc2x 'repoint to first tile in same line mov vscl,hf 'do visible front porch pixels mov tile,phaseflip xor tile,colortable waitvid tile,#0 :skip djnz vx,#:vert 'vertical expand? ror line,linerot 'set next line add line,lineadd wc rol line,linerot if_nc jmp #:line add screen,hc2x 'point to first tile in next line djnz y,#:line 'another tile line? if_z xor interlace,#1 wz 'get interlace and field1 into z test _mode,#%0001 wc 'do visible front porch lines mov x,vf if_nz_and_c add x,#1 call #blank_lines if_nz wrlong invisible,par 'unless interlace and field1, set status to invisible if_z_eq_c call #hsync 'if required, do short line if_z_eq_c mov vscl,hrest if_z_eq_c waitvid burst,sync_high2 if_z_eq_c xor phaseflip,phasemask call #vsync_high 'do high vsync pulses movs vsync1,#sync_low1 'do low vsync pulses movs vsync2,#sync_low2 call #vsync_low call #vsync_high 'do high vsync pulses if_nz mov vscl,hhalf 'if odd frame, do half line if_nz waitvid burst,sync_high2 if_z jmp #field 'if interlace and field1, display field2 jmp #superfield 'else, new superfield ' ' ' Blank lines ' blank_lines call #hsync 'do hsync xor tile,colortable 'do background waitvid tile,#0 djnz x,#blank_lines blank_lines_ret ret ' ' ' Horizontal sync ' hsync test _mode,#%0001 wc 'if pal, toggle phaseflip if_c xor phaseflip,phasemask mov vscl,sync_scale1 'do hsync mov tile,phaseflip xor tile,burst waitvid tile,sync_normal mov vscl,hvis 'setup in case blank line mov tile,phaseflip hsync_ret ret ' ' ' Vertical sync ' vsync_high movs vsync1,#sync_high1 'vertical sync movs vsync2,#sync_high2 vsync_low mov x,vrep vsyncx mov vscl,sync_scale1 vsync1 waitvid burst,sync_high1 mov vscl,sync_scale2 vsync2 waitvid burst,sync_high2 djnz x,#vsyncx vsync_low_ret vsync_high_ret ret ' ' ' Tasks - performed in sections during invisible back porch lines ' tasks mov t1,par 'load parameters movd :par,#_enable '(skip _status) mov t2,#paramcount - 1 :load add t1,#4 :par rdlong 0,t1 add :par,d0 djnz t2,#:load '+119 mov t1,_pins 'set video pins and directions test t1,#$08 wc if_nc mov t2,pins0 if_c mov t2,pins1 test t1,#$40 wc shr t1,#1 shl t1,#3 shr t2,t1 movs vcfg,t2 shr t1,#6 movd vcfg,t1 shl t1,#3 and t2,#$FF shl t2,t1 if_nc mov dira,t2 if_nc mov dirb,#0 if_c mov dira,#0 if_c mov dirb,t2 '+18 tjz _enable,#disabled '+2, disabled? jmpret taskptr,taskret '+1=140, break and return later movs :rd,#wtab 'load ntsc/pal metrics from word table movd :wr,#hvis mov t1,#wtabx - wtab test _mode,#%0001 wc :rd mov t2,0 add :rd,#1 if_nc shl t2,#16 shr t2,#16 :wr mov 0,t2 add :wr,d0 djnz t1,#:rd '+54 if_nc movs :ltab,#ltab 'load ntsc/pal metrics from long table if_c movs :ltab,#ltab+1 movd :ltab,#fcolor mov t1,#(ltabx - ltab) >> 1 :ltab mov 0,0 add :ltab,d0s1 djnz t1,#:ltab '+17 rdlong t1,#0 'get CLKFREQ shr t1,#1 'if CLKFREQ < 16MHz, cancel _broadcast cmp t1,m8 wc if_c mov _broadcast,#0 shr t1,#1 'if CLKFREQ < color frequency * 4, disable cmp t1,fcolor wc if_c jmp #disabled '+11 jmpret taskptr,taskret '+1=83, break and return later mov t1,fcolor 'set ctra pll to fcolor * 16 call #divide 'if ntsc, set vco to fcolor * 32 (114.5454 MHz) test _mode,#%0001 wc 'if pal, set vco to fcolor * 16 (70.9379 MHz) if_c movi ctra,#%00001_111 'select fcolor * 16 output (ntsc=/2, pal=/1) if_nc movi ctra,#%00001_110 if_nc shl t2,#1 mov frqa,t2 '+147 jmpret taskptr,taskret '+1=148, break and return later mov t1,_broadcast 'set ctrb pll to _broadcast mov t2,#0 'if 0, turn off ctrb tjz t1,#:off min t1,m8 'limit from 8MHz to 128MHz max t1,m128 mov t2,#%00001_100 'adjust _broadcast to be within 4MHz-8MHz :scale shr t1,#1 '(vco will be within 64MHz-128MHz) cmp m8,t1 wc if_c add t2,#%00000_001 if_c jmp #:scale :off movi ctrb,t2 call #divide mov frqb,t2 '+165 jmpret taskptr,taskret '+1=166, break and return later mov t1,#%10100_000 'set video configuration test _pins,#$01 wc '(swap broadcast/baseband output bits?) if_c or t1,#%01000_000 test _mode,#%1000 wc '(strip chroma from broadcast?) if_nc or t1,#%00010_000 test _mode,#%0100 wc '(strip chroma from baseband?) if_nc or t1,#%00001_000 and _auralcog,#%111 '(set aural cog) or t1,_auralcog movi vcfg,t1 '+10 mov hx,_hx 'compute horizontal metrics shl hx,#8 or hx,_hx shl hx,#4 mov hc2x,_ht shl hc2x,#1 mov t1,_ht mov t2,_hx call #multiply mov hf,hvis sub hf,t1 shr hf,#1 wc mov hb,_ho addx hb,hf sub hf,_ho '+52 mov t1,_vt 'compute vertical metrics mov t2,_vx call #multiply test _mode,#%10000 wc 'consider tile size muxc linerot,#1 mov lineadd,lineinc if_c shr lineadd,#1 if_c shl t1,#1 test _mode,#%0010 wc 'consider interlace if_c shr t1,#1 mov vf,vvis sub vf,t1 shr vf,#1 wc neg vb,_vo addx vb,vf add vf,_vo '+53 xor _mode,#%0010 '+1, flip interlace bit for display :colors jmpret taskptr,taskret '+1=117/160, break and return later mov t1,#13 'load next 13 colors into colortable :colorloop mov t2,:colorreg '5 times = 65 (all 64 colors loaded) shr t2,#9-2 and t2,#$FC add t2,_colors :colorreg rdlong colortable,t2 add :colorreg,d0 andn :colorreg,d6 djnz t1,#:colorloop '+158 jmp #:colors '+1, keep loading colors ' ' ' Divide t1/CLKFREQ to get frqa or frqb value into t2 ' divide rdlong m1,#0 'get CLKFREQ mov m2,#32+1 :loop cmpsub t1,m1 wc rcl t2,#1 shl t1,#1 djnz m2,#:loop divide_ret ret '+140 ' ' ' Multiply t1 * t2 * 16 (t1, t2 = bytes) ' multiply shl t2,#8+4-1 mov m1,#8 :loop shr t1,#1 wc if_c add t1,t2 djnz m1,#:loop multiply_ret ret '+37 ' ' ' Disabled - reset status, nap ~4ms, try again ' disabled mov ctra,#0 'reset ctra mov ctrb,#0 'reset ctrb mov vcfg,#0 'reset video wrlong outa,par 'set status to disabled rdlong t1,#0 'get CLKFREQ shr t1,#8 'nap for ~4ms min t1,#3 add t1,cnt waitcnt t1,#0 jmp #entry 'reload parameters ' ' ' Initialized data ' m8 long 8_000_000 m128 long 128_000_000 d0 long 1 << 9 << 0 d6 long 1 << 9 << 6 d0s1 long 1 << 9 << 0 + 1 << 1 interlace long 0 invisible long 1 visible long 2 phaseflip long $00000000 phasemask long $F0F0F0F0 line long $00060000 lineinc long $10000000 linerot long 0 pins0 long %11110000_01110000_00001111_00000111 pins1 long %11111111_11110111_01111111_01110111 sync_high1 long %0101010101010101010101_101010_0101 sync_high2 long %01010101010101010101010101010101 'used for black sync_low1 long %1010101010101010101010101010_0101 sync_low2 long %01_101010101010101010101010101010 ' ' ' NTSC/PAL metrics tables ' ntsc pal ' ---------------------------------------------- wtab word lntsc - sntsc, lpal - spal 'hvis word lntsc / 2 - sntsc, lpal / 2 - spal 'hrest word lntsc / 2, lpal / 2 'hhalf word 243, 286 'vvis word 10+0, 18 'vinv word 6, 5 'vrep word $02_8A, $02_AA 'burst wtabx ltab long fntsc 'fcolor long fpal long sntsc >> 4 << 12 + sntsc 'sync_scale1 long spal >> 4 << 12 + spal long 67 << 12 + lntsc / 2 - sntsc 'sync_scale2 long 79 << 12 + lpal / 2 - spal long %0101_00000000_01_10101010101010_0101 'sync_normal long %010101_00000000_01_101010101010_0101 ltabx ' ' ' Uninitialized data ' taskptr res 1 'tasks taskret res 1 t1 res 1 t2 res 1 m1 res 1 m2 res 1 x res 1 'display y res 1 hf res 1 hb res 1 vf res 1 vb res 1 hx res 1 vx res 1 hc2x res 1 screen res 1 tile res 1 pixels res 1 lineadd res 1 hvis res 1 'loaded from word table hrest res 1 hhalf res 1 vvis res 1 vinv res 1 vrep res 1 burst res 1 fcolor res 1 'loaded from long table sync_scale1 res 1 sync_scale2 res 1 sync_normal res 1 ' ' ' Parameter buffer ' _enable res 1 '0/non-0 read-only _pins res 1 '%pppmmmm read-only _mode res 1 '%tccip read-only _screen res 1 '@word read-only _colors res 1 '@long read-only _ht res 1 '1+ read-only _vt res 1 '1+ read-only _hx res 1 '4+ read-only _vx res 1 '1+ read-only _ho res 1 '0+- read-only _vo res 1 '0+- read-only _broadcast res 1 '0+ read-only _auralcog res 1 '0-7 read-only fit colortable 'fit underneath colortable ($180-$1BF) '' ''___ ''VAR 'TV parameters - 14 contiguous longs '' '' long tv_status '0/1/2 = off/invisible/visible read-only '' long tv_enable '0/non-0 = off/on write-only '' long tv_pins '%pppmmmm = pin group, pin group mode write-only '' long tv_mode '%tccip = tile,chroma,interlace,ntsc/pal write-only '' long tv_screen 'pointer to screen (words) write-only '' long tv_colors 'pointer to colors (longs) write-only '' long tv_ht 'horizontal tiles write-only '' long tv_vt 'vertical tiles write-only '' long tv_hx 'horizontal tile expansion write-only '' long tv_vx 'vertical tile 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 '' ''The preceding VAR section may be copied into your code. ''After setting variables, do start(@tv_status) to start driver. '' ''All parameters are reloaded each superframe, allowing you to make live ''changes. To minimize flicker, correlate changes with tv_status. '' ''Experimentation may be required to optimize some parameters. '' ''Parameter descriptions: '' _________ '' tv_status '' '' driver sets this to indicate status: '' 0: driver disabled (tv_enable = 0 or CLKFREQ < requirement) '' 1: currently outputting invisible sync data '' 2: currently outputting visible screen data '' _________ '' tv_enable '' '' 0: disable (pins will be driven low, reduces power) '' non-0: enable '' _______ '' tv_pins '' '' bits 6..4 select pin group: '' %000: pins 7..0 '' %001: pins 15..8 '' %010: pins 23..16 '' %011: pins 31..24 '' %100: pins 39..32 '' %101: pins 47..40 '' %110: pins 55..48 '' %111: pins 63..56 '' '' bits 3..0 select pin group mode: '' %0000: %0000_0111 - baseband '' %0001: %0000_0111 - broadcast '' %0010: %0000_1111 - baseband + chroma '' %0011: %0000_1111 - broadcast + aural '' %0100: %0111_0000 broadcast - '' %0101: %0111_0000 baseband - '' %0110: %1111_0000 broadcast + aural - '' %0111: %1111_0000 baseband + chroma - '' %1000: %0111_0111 broadcast baseband '' %1001: %0111_0111 baseband broadcast '' %1010: %0111_1111 broadcast baseband + chroma '' %1011: %0111_1111 baseband broadcast + aural '' %1100: %1111_0111 broadcast + aural baseband '' %1101: %1111_0111 baseband + chroma broadcast '' %1110: %1111_1111 broadcast + aural baseband + chroma '' %1111: %1111_1111 baseband + chroma broadcast + aural '' ----------------------------------------------------------- '' active pins top nibble bottom nibble '' '' the baseband signal nibble is arranged as: '' bit 3: chroma signal for s-video (attach via 560-ohm resistor) '' bits 2..0: baseband video (sum 270/560/1100-ohm resistors to form 75-ohm 1V signal) '' '' the broadcast signal nibble is arranged as: '' bit 3: aural subcarrier (sum 560-ohm resistor into network below) '' bits 2..0: visual carrier (sum 270/560/1100-ohm resistors to form 75-ohm 1V signal) '' _______ '' tv_mode '' '' bit 4 selects between 16x16 and 16x32 pixel tiles: '' 0: 16x16 pixel tiles (tileheight = 16) '' 1: 16x32 pixel tiles (tileheight = 32) '' '' bit 3 controls chroma mixing into broadcast: '' 0: mix chroma into broadcast (color) '' 1: strip chroma from broadcast (black/white) '' '' bit 2 controls chroma mixing into baseband: '' 0: mix chroma into baseband (composite color) '' 1: strip chroma from baseband (black/white or s-video) '' '' bit 1 controls interlace: '' 0: progressive scan (243 display lines for NTSC, 286 for PAL) '' less flicker, good for motion '' 1: interlaced scan (486 display lines for NTSC, 572 for PAL) '' doubles the vertical display lines, good for text '' '' bit 0 selects NTSC or PAL format '' 0: NTSC '' 3016 horizontal display ticks '' 243 or 486 (interlaced) vertical display lines '' CLKFREQ must be at least 14_318_180 (4 * 3_579_545 Hz)* '' 1: PAL '' 3692 horizontal display ticks '' 286 or 572 (interlaced) vertical display lines '' CLKFREQ must be at least 17_734_472 (4 * 4_433_618 Hz)* '' '' * driver will disable itself while CLKFREQ is below requirement '' _________ '' tv_screen '' '' pointer to words which define screen contents (left-to-right, top-to-bottom) '' number of words must be tv_ht * tv_vt '' each word has two bitfields: a 6-bit colorset ptr and a 10-bit pixelgroup ptr '' bits 15..10: select the colorset* for the associated pixel tile '' bits 9..0: select the pixelgroup** address %ppppppppppcccc00 (p=address, c=0..15) '' '' * colorsets are longs which each define four 8-bit colors '' '' ** pixelgroups are longs which define (left-to-right, top-to-bottom) the 2-bit '' (four color) pixels that make up a 16x16 or a 32x32 pixel tile '' _________ '' tv_colors '' '' pointer to longs which define colorsets '' number of longs must be 1..64 '' each long has four 8-bit fields which define colors for 2-bit (four color) pixels '' first long's bottom color is also used as the screen background color '' 8-bit color fields are as follows: '' bits 7..4: chroma data (0..15 = blue..green..red..)* '' bit 3: controls chroma modulation (0=off, 1=on) '' bits 2..0: 3-bit luminance level: '' values 0..1: reserved for sync - don't use '' values 2..7: valid luminance range, modulation adds/subtracts 1 (beware of 7) '' value 0 may be modulated to produce a saturated color toggling between levels 1 and 7 '' '' * because of TV's limitations, it doesn't look good when chroma changes abruptly - '' rather, use luminance - change chroma only against a black or white background for '' best appearance '' _____ '' tv_ht '' '' horizontal number pixel tiles - must be at least 1 '' practical limit is 40 for NTSC, 50 for PAL '' _____ '' tv_vt '' '' vertical number of pixel tiles - must be at least 1 '' practical limit is 13 for NTSC, 15 for PAL (26/30 max for interlaced NTSC/PAL) '' _____ '' tv_hx '' '' horizontal tile expansion factor - must be at least 3 for NTSC, 4 for PAL '' '' make sure 16 * tv_ht * tv_hx + ||tv_ho + 32 is less than the horizontal display ticks '' _____ '' tv_vx '' '' vertical tile expansion factor - must be at least 1 '' '' make sure * tv_vt * tv_vx + ||tv_vo + 1 is less than the display lines '' _____ '' tv_ho '' '' horizontal offset in ticks - pos/neg value (0 for centered image) '' shifts the display right/left '' _____ '' tv_vo '' '' vertical offset in lines - pos/neg value (0 for centered image) '' shifts the display up/down '' ____________ '' tv_broadcast '' '' broadcast frequency expressed in Hz (ie channel 2 is 55_250_000) '' if 0, modulator is turned off - saves power '' '' broadcasting requires CLKFREQ to be at least 16_000_000 '' while CLKFREQ is below 16_000_000, modulator will be turned off '' ___________ '' tv_auralcog '' '' selects cog to supply aural fm signal - 0..7 '' uses ctra pll output from selected cog '' '' in NTSC, the offset frequency must be 4.5MHz and the max bandwidth +-25KHz '' in PAL, the offset frequency and max bandwidth vary by PAL type \ No newline at end of file