Skip to content

Instantly share code, notes, and snippets.

@samneggs
Created June 11, 2026 15:32
Show Gist options
  • Select an option

  • Save samneggs/fa37417e69c864c9d7914effe3cdde0b to your computer and use it in GitHub Desktop.

Select an option

Save samneggs/fa37417e69c864c9d7914effe3cdde0b to your computer and use it in GitHub Desktop.
Star Castle with DVI output on Fruit Jam
# Star Castle — DVI port (640×480 BGR233, RP2350 HSTX)
from dvi_rp2_hstx_frame_sync_v2 import DVI_RP2_HSTX
import colors as rv_colors
from gamepadfast import Gamepad
import machine, framebuf
from machine import Pin, PWM
from uctypes import addressof
from time import sleep, ticks_us, ticks_diff, ticks_ms, sleep_ms
import gc, _thread, array
from sys import exit
from micropython import const
from random import randint
from math import sin,cos,tan,radians,sqrt,atan2
MAXSCREEN_X = const(640)
MAXSCREEN_Y = const(480)
SCREEN_W = const(640)
SCREEN_H = const(480)
HALFSCREEN_X = const(MAXSCREEN_X//2)
HALFSCREEN_Y = const(MAXSCREEN_Y//2)
SCALE = const(13)
NUM_STARS = const(100)
NUM_MISSILES= const(20)
# ── BGR233 palette ────────────────────────────────────────────────────────────
BLACK = const(0x00)
WHITE = const(0xFF)
BLUE = const(0b000_000_11)
SKYBLUE = const(0b100_100_11)
YELLOW = const(0b111_111_00)
RED = const(0b111_000_00)
ORANGE = const(0b111_100_00)
BACKGROUND = const(BLACK)
# ── Star dimming levels (RGB332: rrr_ggg_bb), 4 shades of white ──────────────
# Level 0 (dim) → Level 3 (bright)
STAR_LUT = array.array('B', (0b010_010_00, # 0x48 ~25% white
0b100_100_01, # 0x91 ~50% white
0b110_110_10, # 0xDA ~75% white
0b111_111_11)) # 0xFF 100% white
# ── Gamepad array layout (shared with DVI template) ──────────────────────────
GAMEPAD = array.array('i', [0, 0, 0]) # x, y, delay fire
GAMEPAD_X = const(0)
GAMEPAD_Y = const(1)
GAMEPAD_DELAY = const(2)
GAMEPAD_RIGHT = const(0b0100000)
GAMEPAD_LEFT = const(0b0000100)
GAMEPAD_UP = const(0b1000000)
GAMEPAD_DOWN = const(0b0000010)
GAMEPAD_SELECT = const(0b0000001)
PLAYER_PARAMS = const(10)
X = const(0)
Y = const(1)
DEG = const(2)
VX = const(3)
VY = const(4)
AX = const(5)
AY = const(6)
M = const(7)
GAME_PARAMS = const(10)
FPS = const(0)
LIVES = const(1)
HIT_CANNON = const(2)
DEG_CANNON = const(3)
FIRE_CANNON = const(4)
SCORE = const(5)
SHIELD_PARAMS = const(10)
SHIELD_LIFE = const(3)
SHIELD_DIR = const(4)
SHIELD_RAD = const(5)
MISSILE_PARAMS = const(10)
MISS_LIFE = const(2)
STARS_PARAMS = const(10)
STAR_COLOR = const(2)
STAR_DIR = const(3)
EXPLODE_PARAMS = const(10)
EXP_LIFE = const(2)
EXP_COLOR = const(5)
MINE_SHIELD = const(5)
# ── GAME control array ────────────────────────────────────────────────────────
GAME_CTL = array.array('i', [0] * 10)
GAME_CTL_EXIT = const(0)
# ── Hardware init ─────────────────────────────────────────────────────────────
machine.freq(246_000_000)
machine.mem32[0x40010058] = 2 << 16 # HSTX CLK / 2
machine.mem32[0x40010054] = 1 << 11 # HSTX CLK uses SYS CLK
fb = bytearray(SCREEN_W * SCREEN_H) # single DVI framebuffer (BGR233)
gamepad = Gamepad()
SCREEN = framebuf.FrameBuffer(fb, SCREEN_W, SCREEN_H, framebuf.GS8)
#draw_num = Draw_number(fb, SCREEN_W, 1)
SHIP_POINTS = const(16)
SHIP_DEG = array.array('H',(0, 306, 180, 54, 0, 333, 349, 333, 189, 333, 0, 27, 11, 27, 171, 27) )
SHIP_RADIUS = array.array('H',(0, 17, 8, 17, 0, 4, 10, 9, 12, 4, 0, 4, 10, 9, 12, 4) )
char_map=array.array('b',(
0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00, # U+0030 (0)
0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00, # U+0031 (1)
0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00, # U+0032 (2)
0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00, # U+0033 (3)
0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00, # U+0034 (4)
0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00, # U+0035 (5)
0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00, # U+0036 (6)
0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00, # U+0037 (7)
0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00, # U+0038 (8)
0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00)) # U+0039 (9)
@micropython.viper
def show_num_viper(num:int,x_offset:int,y_offset:int,color:int):
char_ptr = ptr8(char_map)
screen_ptr = ptr8(SCREEN)
size = 1 # 1,2,3
char = 0
offset = MAXSCREEN_X*y_offset+x_offset
first = 1
while num > 0 or first:
first = 0
total = num//10
digit = num - (total * 10)
num = total
for y in range(8):
row_data = char_ptr[digit*8+y]
for x in range(8):
if row_data & (1<<x) > 0:
addr = size*y*MAXSCREEN_X+x-(char*8)+offset
screen_ptr[addr] = color
if size>1:
screen_ptr[MAXSCREEN_X+addr] = color
if size>2:
screen_ptr[2*MAXSCREEN_X+addr] = color
char += 1
def init_imath():
global ISIN, ICOS
scale = 1 << SCALE
ISIN = array.array('i', (int(sin(radians(i)) * scale) for i in range(360)))
ICOS = array.array('i', (int(cos(radians(i)) * scale) for i in range(360)))
# ── Wait-for-VBlank + Fast fill (asm_thumb, 192 bytes per iteration) ──────────
@micropython.asm_thumb
def fill_asm(r0, r1, r2): # (buffer_addr, 8-bit_color, flag_addr)
# ── Phase 1: spin until DMA frame marker sets _frame_flag[0] != 0 ────────
label(WAIT)
ldr(r3, [r2, 0]) # r3 = _frame_flag[0]
cmp(r3, 0)
beq(WAIT) # keep spinning while flag == 0
mov(r3, 0)
str(r3, [r2, 0]) # clear flag (re-arm for next frame)
# ── Phase 2: expand 8-bit color to 32-bit fill word ──────────────────────
mov(r3, r1) # r3 = color
lsl(r2, r1, 8)
orr(r3, r2) # r3 = color | (color << 8)
lsl(r2, r3, 16)
orr(r3, r2) # r3 = 32-bit fill word (color replicated 4x)
mov(r1, r0) # r1 = write ptr (STM base)
mov(r2, r3)
mov(r4, r3)
mov(r5, r3)
mov(r6, r3)
mov(r7, r3)
movwt(r0, SCREEN_W * SCREEN_H // 192)
# ── Phase 3: store 192 bytes per iteration via 8x STM r1!,{r2-r7} ────────
label(LOOP)
data(2, 0b11000_001_11111100)
data(2, 0b11000_001_11111100)
data(2, 0b11000_001_11111100)
data(2, 0b11000_001_11111100)
data(2, 0b11000_001_11111100)
data(2, 0b11000_001_11111100)
data(2, 0b11000_001_11111100)
data(2, 0b11000_001_11111100)
sub(r0, 1)
bne(LOOP)
@micropython.viper
def read_pot():
isin = ptr32(ISIN)
icos = ptr32(ICOS)
player = ptr32(PLAYER)
g_pad = ptr32(GAMEPAD)
ctrl = ptr32(GAME_CTL)
gamepad.read()
buttons = int(gamepad.buttons)
x_inc = int(gamepad.x) >> 6
y_inc = int(gamepad.y) >> 6
if not (buttons & GAMEPAD_SELECT): # SELECT = quit
ctrl[GAME_CTL_EXIT] = 1
if (g_pad[GAMEPAD_DELAY] == 1) and (not (buttons & GAMEPAD_RIGHT)): # RIGHT = fire
init_missile()
g_pad[GAMEPAD_DELAY] = 0
# Clamp analogue deadband (same threshold, values from gamepad.x/y)
x_dead = x_inc if x_inc >= 0 else (-1 * x_inc)
if x_dead < 2: x_inc = 0
if y_inc > 2: y_inc = 0 # no reverse
player[DEG] += x_inc
if player[DEG] >= 360: player[DEG] -= 360
if player[DEG] < 0 : player[DEG] += 360
deg = int(player[DEG])
x = (600*y_inc * int(icos[deg]))>>SCALE
y = (600*y_inc * int(isin[deg]))>>SCALE
player[VX] += x
player[VY] += y
def init_player():
global PLAYER , SHIP_COORDS, MISSILES, CANNON_COORDS
PLAYER = array.array('i',0 for _ in range(PLAYER_PARAMS))
SHIP_COORDS = array.array('i',0 for _ in range(50*2))
CANNON_COORDS = array.array('i',0 for _ in range(50*2))
MISSILES = array.array('i',0 for _ in range(MISSILE_PARAMS*NUM_MISSILES))
PLAYER[X] = 10<<SCALE
PLAYER[Y] = 10<<SCALE
PLAYER[DEG]= 220
PLAYER[VX] = 0<<SCALE
PLAYER[VY] = 0<<SCALE
PLAYER[AX] = 0
PLAYER[AY] = 0
PLAYER[M] = 0
@micropython.viper
def player_reset():
miss = ptr32(MISSILES)
player = ptr32(PLAYER)
player[X] = 10<<SCALE
player[Y] = 10<<SCALE
player[DEG]= 220
for index in range(NUM_MISSILES):
i = index * MISSILE_PARAMS
miss[i + MISS_LIFE] = 0
def init_game():
global GAME, SHIELD, STARS, COLORS, EXPLODE, FPS_ARRY, CANNON_MISL, MINES
GAME = array.array('i',0 for _ in range(GAME_PARAMS))
SHIELD = array.array('i',0 for _ in range(SHIELD_PARAMS*12*3))
STARS = array.array('i',0 for _ in range(STARS_PARAMS*NUM_STARS))
COLORS = array.array('H',(YELLOW,ORANGE,RED))
EXPLODE = array.array('i',0 for _ in range(EXPLODE_PARAMS*20))
CANNON_MISL = array.array('i',0 for _ in range(MISSILE_PARAMS))
MINES = array.array('i',0 for _ in range(MISSILE_PARAMS*3))
FPS_ARRY = bytearray(100)
GAME[FPS] = 0
GAME[LIVES] = 3
for index in range(NUM_STARS):
i = index * STARS_PARAMS
STARS[i + X] = randint(0,MAXSCREEN_X-1)
STARS[i + Y] = randint(0,MAXSCREEN_Y-1)
STARS[i + STAR_COLOR] = randint(0,3)
if STARS[i + STAR_COLOR] > 1:
STARS[i + STAR_DIR] = -1
else:
STARS[i + STAR_DIR] = 1
def init_shields():
global SHIELD
for index in range(12*3):
i = index * SHIELD_PARAMS
deg = (index % 12) * 30
radius = 40 + (20 * (index//12))
SHIELD[i + X] = int(cos(radians(deg))*radius + MAXSCREEN_X//2)
SHIELD[i + Y] = int(sin(radians(deg))*radius + MAXSCREEN_Y//2)
SHIELD[i + DEG] = deg
SHIELD[i + SHIELD_LIFE] = 2
SHIELD[i + SHIELD_DIR] = 5
if index>11 and index<24:
SHIELD[i + SHIELD_DIR] = -5
SHIELD[i + SHIELD_RAD] = radius
@micropython.viper
def init_missile():
miss = ptr32(MISSILES)
player = ptr32(PLAYER)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
game = ptr32(GAME)
if game[LIVES] < 1: return
x = player[X]
y = player[Y]
vx = player[VX]
vy = player[VY]
deg = player[DEG] + 180
if deg >= 360: deg -= 360
for index in range(NUM_MISSILES):
i = index * MISSILE_PARAMS
if miss[i + MISS_LIFE] == 0:
miss[i + MISS_LIFE] = 2000
miss[i + X] = icos[deg]*10 + x
miss[i + Y] = isin[deg]*10 + y
miss[i + VX] = (icos[deg])>>2
miss[i + VY] = (isin[deg])>>2
return
@micropython.viper
def init_cannon():
miss = ptr32(CANNON_MISL)
game = ptr32(GAME)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
if game[LIVES] < 1 or game[HIT_CANNON] > 0: return
x = HALFSCREEN_X << SCALE
y = HALFSCREEN_Y << SCALE
deg = game[DEG_CANNON] + 180
if deg >= 360: deg -= 360
if miss[MISS_LIFE] == 0:
miss[MISS_LIFE] = 2000
miss[X] = icos[deg]*10 + x
miss[Y] = isin[deg]*10 + y
miss[VX] = (icos[deg])>>2
miss[VY] = (isin[deg])>>2
@micropython.viper
def init_mine():
mine = ptr32(MINES)
mine[(0*MISSILE_PARAMS) + MINE_SHIELD] = 1
mine[(1*MISSILE_PARAMS) + MINE_SHIELD] = 4
mine[(2*MISSILE_PARAMS) + MINE_SHIELD] = 8
@micropython.viper
def move_mine():
shield = ptr32(SHIELD)
mine = ptr32(MINES)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
player = ptr32(PLAYER)
game = ptr32(GAME)
for index in range(3):
i = index * MISSILE_PARAMS
shield_pos = mine[i + MINE_SHIELD]
shield_index = shield_pos * SHIELD_PARAMS
if shield_pos < 24 and shield[shield_index + DEG] == shield[(12*SHIELD_PARAMS+shield_index) + DEG] and int(randint(1,3))==3:
shield_pos += 12
mine[i + MINE_SHIELD] = shield_pos
if shield[(12*SHIELD_PARAMS+shield_index) + SHIELD_LIFE] == 0:
mine[i + MINE_SHIELD] == 36
if shield_pos < 36:
shield_index = shield_pos * MISSILE_PARAMS
mine[i + X] = shield[shield_index + X] << SCALE
mine[i + Y] = shield[shield_index + Y] << SCALE
if shield_pos > 23 and shield_pos < 36 and int(randint(1,20))==20:
mine[i + MINE_SHIELD] = 36
deg = shield[shield_index + DEG]
mine[i + VX] = (icos[deg])>>1
mine[i + VY] = (isin[deg])>>1
mine[i + MISS_LIFE] = 400
if mine[i + MINE_SHIELD] == 36 and mine[i + MISS_LIFE] > 0 :
mine[i + MISS_LIFE] -= 1
if mine[i + MISS_LIFE] < 1:
mine[(index*MISSILE_PARAMS) + MINE_SHIELD] = 1
continue
mine[i + X] += mine[i + VX]
mine[i + Y] += mine[i + VY]
if mine[i + X] < player[X] and mine[i + VX] < 20000: mine[i + VX] += 1000 # 400
if mine[i + X] > player[X] and mine[i + VX] > -20000: mine[i + VX] -= 1000
if mine[i + Y] < player[Y] and mine[i + VY] < 20000: mine[i + VY] += 1000
if mine[i + Y] > player[Y] and mine[i + VY] > -20000: mine[i + VY] -= 1000
# --- ADD: bleed off tangential velocity (breaks orbit, preserves loops) ---
mine[i + VX] -= mine[i + VX] >> 5 # ~3% drag per tick
mine[i + VY] -= mine[i + VY] >> 5
if mine[i + X] < 0 or mine[i + X] > MAXSCREEN_X<<SCALE or mine[i + Y] < 0 or mine[i + Y] > MAXSCREEN_Y<<SCALE:
mine[i + MINE_SHIELD] = 1
if game[LIVES] < 1: continue
if mine[i + X] > player[X] - 20000 and mine[i + X] < player[X] + 20000 and mine[i + Y] > player[Y]-20000 and mine[i + Y] < player[Y]+20000:
init_explode(player[X],player[Y],30,10000)
game[LIVES] -= 1
player_reset()
init_mine()
@micropython.viper
def move_missile():
miss = ptr32(MISSILES)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
shield = ptr32(SHIELD)
game = ptr32(GAME)
mine = ptr32(MINES)
for index in range(NUM_MISSILES): # calc missile positions
i = index * MISSILE_PARAMS
if miss[i + MISS_LIFE] > 0:
miss[i + MISS_LIFE] -= 1
x = miss[i + X]
y = miss[i + Y]
x += miss[i + VX]
y += miss[i + VY]
if x < 0 or y < 0 or x > MAXSCREEN_X<<SCALE or y > MAXSCREEN_Y<<SCALE:
miss[i + MISS_LIFE] = 0
continue
miss[i + X] = x
miss[i + Y] = y
x >>= SCALE
y >>= SCALE
if x > HALFSCREEN_X-3 and x < HALFSCREEN_X+3 and y > HALFSCREEN_Y-4 and y < HALFSCREEN_Y+4: # hit cannon
miss[i + MISS_LIFE] = 0
init_explode(miss[i + X],miss[i + Y],100,10000)
game[HIT_CANNON] = 100
player_reset()
game[SCORE] += 1440
game[LIVES] += 1
break
for index2 in range(12*3): # missile to shield collision
i2 = index2 * SHIELD_PARAMS
if shield[i2 + SHIELD_LIFE] > 0:
s_x1 = shield[i2 + X]
s_y1 = shield[i2 + Y]
if index2 == 11 or index2 == 23 or index2 == 35:
s_x2 = shield[(index2-11) * SHIELD_PARAMS + X ]
s_y2 = shield[(index2-11) * SHIELD_PARAMS + Y ]
else:
s_x2 = shield[i2 + X + SHIELD_PARAMS]
s_y2 = shield[i2 + Y + SHIELD_PARAMS]
if s_x1 > s_x2: s_x1,s_x2 = s_x2,s_x1
if s_y1 > s_y2: s_y1,s_y2 = s_y2,s_y1
if x > s_x1-1 and x < s_x2+1 and y > s_y1-1 and y < s_y2+1: # hit shield
shield[i2 + SHIELD_LIFE] -= 1
miss[i + MISS_LIFE] = 0
if shield[i2 + SHIELD_LIFE] == 0:
init_explode(miss[i + X],miss[i + Y],20,5000) # 10, 10000
score(index2)
break
for index3 in range(3):
i = index3 * MISSILE_PARAMS
mine_x = mine[i + X] >> SCALE
mine_y = mine[i + Y] >> SCALE
if x > mine_x-3 and x < mine_x+3 and y > mine_y-3 and y < mine_y+3: # hit mine
mine[i + MINE_SHIELD] = 1
init_explode(mine[i + X],mine[i + Y],20,5000)
@micropython.viper
def score(shield:int):
game = ptr32(GAME)
if shield > 23:
game[SCORE] += 10
elif shield < 12:
game[SCORE] += 30
else:
game[SCORE] += 20
@micropython.viper
def move_cannon_misl():
miss = ptr32(CANNON_MISL)
game = ptr32(GAME)
player = ptr32(PLAYER)
player_x = player[X]>>SCALE
player_y = player[Y]>>SCALE
if miss[MISS_LIFE] > 0:
miss[MISS_LIFE] -= 1
x = miss[X]
y = miss[Y]
x += miss[VX]
y += miss[VY]
if x < 0 or y < 0 or x > MAXSCREEN_X<<SCALE or y > MAXSCREEN_Y<<SCALE:
miss[MISS_LIFE] = 0
return
miss[X] = x
miss[Y] = y
x >>= SCALE
y >>= SCALE
if x > player_x-4 and x < player_x+4 and y > player_y-4 and y < player_y+4: # hit player
miss[MISS_LIFE] = 0
init_explode(miss[X],miss[Y],30,10000)
game[LIVES] -= 1
player_reset()
@micropython.viper
def move_cannon():
game = ptr32(GAME)
player = ptr32(PLAYER)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
x = (player[X] >> SCALE) - HALFSCREEN_X
y = (player[Y] >> SCALE) - HALFSCREEN_Y
deg = game[DEG_CANNON]
cross = x * isin[deg] - y * icos[deg]
if cross > 0:
deg += 1
elif cross < 0:
deg -= 1
elif icos[deg] * x + isin[deg] * y > 0:
deg += 1 # exactly 180° off — break the tie
if deg > 359: deg -= 360
if deg < 0: deg += 360
game[DEG_CANNON] = deg
@micropython.viper
def move():
player = ptr32(PLAYER)
deg = ptr16(SHIP_DEG)
radius = ptr16(SHIP_RADIUS)
coords = ptr32(SHIP_COORDS)
cannon = ptr32(CANNON_COORDS)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
shield = ptr32(SHIELD)
stars = ptr32(STARS)
game = ptr32(GAME)
size = 1
player[X] += player[VX]
player[Y] += player[VY]
player[VX] -= player[VX]>>3 # auto slow to stop
player[VY] -= player[VY]>>3
if player[X] < 0: player[X] = MAXSCREEN_X<<SCALE
if player[Y] < 0: player[Y] = MAXSCREEN_Y<<SCALE
if player[X] > MAXSCREEN_X<<SCALE : player[X] = 0
if player[Y] > MAXSCREEN_Y<<SCALE : player[Y] = 0
x = player[X]
y = player[Y]
for index in range(SHIP_POINTS): # calc ship points
i = index * 2
pt_deg = deg[index] + player[DEG]
if pt_deg >= 360: pt_deg -= 360
if pt_deg < 0 : pt_deg += 360
coords[i] = ((radius[index] * size * icos[pt_deg])>>14)
coords[i+1] = ((radius[index] * size * isin[pt_deg])>>14)
for index in range(SHIP_POINTS): # calc cannon points
i = index * 2
pt_deg = deg[index] + game[DEG_CANNON]
if pt_deg >= 360: pt_deg -= 360
if pt_deg < 0 : pt_deg += 360
cannon[i] = ((radius[index] * size * icos[pt_deg])>>14)
cannon[i+1] = ((radius[index] * size * isin[pt_deg])>>14)
for index in range(12*3): # calc shield points
i = index * SHIELD_PARAMS
shield_rad = shield[i + SHIELD_RAD]
shield_deg = shield[i + DEG] + shield[i + SHIELD_DIR]
if shield_deg < 0: shield_deg += 360
if shield_deg >= 360: shield_deg -= 360
shield[i + DEG] = shield_deg
shield[i + X] = ((icos[shield_deg]*shield_rad)>>SCALE) + MAXSCREEN_X//2
shield[i + Y] = ((isin[shield_deg]*shield_rad)>>SCALE) + MAXSCREEN_Y//2
x >>= SCALE
y >>= SCALE
for index in range(12*3): # ship/shield collision
i = index * SHIELD_PARAMS
if shield[i + SHIELD_LIFE] > 0:
s_x1 = shield[i + X]
s_y1 = shield[i + Y]
if index == 11 or index == 23 or index == 35:
s_x2 = shield[(index-11) * SHIELD_PARAMS + X ]
s_y2 = shield[(index-11) * SHIELD_PARAMS + Y ]
else:
s_x2 = shield[i + X + SHIELD_PARAMS]
s_y2 = shield[i + Y + SHIELD_PARAMS]
if s_x1 > s_x2: s_x1,s_x2 = s_x2,s_x1
if s_y1 > s_y2: s_y1,s_y2 = s_y2,s_y1
if x > s_x1 and x < s_x2 and y > s_y1 and y < s_y2: # hit
player_deg = player[DEG] + 180
if player_deg >= 360: player_deg -= 360
if player_deg < 0 : player_deg += 360
player[DEG] = player_deg
player[VX] *= -1
player[VY] *= -1
player[X] += player[VX]<<3
player[Y] += player[VY]<<3
@micropython.viper
def check_cannon():
shield = ptr32(SHIELD)
game = ptr32(GAME)
cannon_deg = game[DEG_CANNON]
first = 0
second = 0
third = 0
for index in range(0,36): # 0-12, 13-24, 25-36
i = index * SHIELD_PARAMS
deg = (index % 12) * 30
shield_deg = shield[i + DEG] + 180
if shield_deg > 359: shield_deg -= 360
shield_life = shield[i + SHIELD_LIFE]
if shield_life > 0 or shield_deg > cannon_deg + 15 or shield_deg < cannon_deg - 15: continue
if index < 12:
first = 1
elif index > 24:
third = 1
else:
second = 1
if first+second+third == 3:
game[FIRE_CANNON] = 1
init_cannon()
@micropython.viper
def twinkle_stars():
stars = ptr32(STARS)
for index in range(NUM_STARS):
i = index * STARS_PARAMS
dim = int(stars[i + STAR_COLOR])
dim += stars[i + STAR_DIR]
if dim > 3 or dim < 0:
stars[i + STAR_DIR] *= -1
else:
stars[i + STAR_COLOR] = dim
@micropython.viper
def init_explode(x:int,y:int,life:int,speed:int):
exp = ptr32(EXPLODE)
exp[X] = x
exp[Y] = y
for index in range(1,20):
i = index * EXPLODE_PARAMS
exp[i + X] = x
exp[i + Y] = y
exp[i + VX] = int(randint(-1*speed,speed))
exp[i + VY] = int(randint(-1*speed,speed))
exp[i + EXP_LIFE] = life
exp[i + EXP_COLOR] = 0
@micropython.viper
def explode():
exp = ptr32(EXPLODE)
game = ptr32(GAME)
if game[HIT_CANNON] > 0:
game[HIT_CANNON] -= 1
if game[HIT_CANNON] == 0:
init_shields()
init_mine()
for index in range(1,20):
i = index * EXPLODE_PARAMS
if exp[i + EXP_LIFE] > 0:
exp[i + X] += exp[i + VX]
exp[i + Y] += exp[i + VY]
exp[i + EXP_LIFE] -= 1
@micropython.viper
def draw():
game = ptr32(GAME)
player = ptr32(PLAYER)
coords = ptr32(SHIP_COORDS)
can_miss = ptr32(CANNON_MISL)
stars = ptr32(STARS)
shield = ptr32(SHIELD)
colors = ptr16(COLORS)
miss = ptr32(MISSILES)
exp = ptr32(EXPLODE)
mine = ptr32(MINES)
star_lut = ptr8(STAR_LUT)
#display.wait_frame()
fill_asm(fb, BACKGROUND,FLAG_ADDR)
for index in range(NUM_STARS): # draw stars (white, 4 dim levels)
i = index * STARS_PARAMS
SCREEN.pixel(stars[i+X],stars[i+Y],star_lut[stars[i + STAR_COLOR]])
if game[HIT_CANNON] == 0:
for index in range(12*3): # draw shield
i = index * SHIELD_PARAMS
if shield[i + SHIELD_LIFE] > 0:
x1 = shield[i + X]
y1 = shield[i + Y]
if index == 11 or index == 23 or index == 35:
x2 = shield[(index-11) * SHIELD_PARAMS + X ]
y2 = shield[(index-11) * SHIELD_PARAMS + Y ]
else:
x2 = shield[i + X + SHIELD_PARAMS]
y2 = shield[i + Y + SHIELD_PARAMS]
SCREEN.line(x1,y1,x2,y2,colors[index//12])
for index in range(NUM_MISSILES): # draw missiles
i = index * MISSILE_PARAMS
if miss[i + MISS_LIFE] > 0:
SCREEN.pixel(miss[i + X]>>SCALE, miss[i + Y]>>SCALE, WHITE)
if can_miss[MISS_LIFE] > 0:
cannon_misl_x = can_miss[X]>>SCALE
cannon_misl_y = can_miss[Y]>>SCALE
SCREEN.pixel(cannon_misl_x,cannon_misl_y, WHITE) # draw cannon missile
color = colors[int(randint(0,2))]
for i in range(10):
x = int(randint(-8,8))
y = int(randint(-8,8))
SCREEN.line(cannon_misl_x,cannon_misl_y, cannon_misl_x+x,cannon_misl_y+y,color)
e_x = exp[X]
e_y = exp[Y]
for index in range(1,20): # draw explosion
i = index * EXPLODE_PARAMS
if exp[i + EXP_LIFE] > 0:
SCREEN.line(e_x>>SCALE,e_y>>SCALE,exp[i + X]>>SCALE,exp[i + Y]>>SCALE,colors[(exp[i + EXP_LIFE]//12) % 3])
for index in range(3):
i = index * MISSILE_PARAMS # draw mines
mine_x = mine[i + X] >> SCALE
mine_y = mine[i + Y] >> SCALE
for i in range(10):
x = int(randint(-3,3))
y = int(randint(-3,3))
SCREEN.line(mine_x,mine_y, mine_x+x,mine_y+y,0b011_011_11)
#SCREEN.text('FPS',0,0,0xff)
#show_num_viper(game[FPS],10,10,0xff)
SCREEN.text('SCORE',120,10,0xff)
show_num_viper(game[SCORE],150,20,0xff)
SCREEN.text('LIVES',230,10,0xff)
show_num_viper(game[LIVES],250,20,0xff)
if game[LIVES] == 0:
SCREEN.text('GAME OVER',285,180,0xff)
x = player[X] >> SCALE
y = player[Y] >> SCALE
if game[LIVES] > 0:
SCREEN.poly(x,y,SHIP_COORDS,SKYBLUE) # draw ship
if game[HIT_CANNON] == 0:
SCREEN.poly(HALFSCREEN_X,HALFSCREEN_Y,CANNON_COORDS,0xff) # draw cannon
@micropython.viper
def core0():
init_imath()
init_game()
init_shields()
init_player()
init_mine()
game = ptr32(GAME)
g_pad = ptr32(GAMEPAD)
ctrl = ptr32(GAME_CTL)
pot_ticks = 0
move_ticks = 0
init_miss_ticks = 0
move_miss_ticks = 0
twinkle_ticks = 0
explode_ticks = 0
cannon_ticks = 0
while not ctrl[GAME_CTL_EXIT]:
sleep(0.001)
gticks = int(ticks_ms())
if gticks-pot_ticks>30:
pot_ticks = int(ticks_ms())
read_pot()
if gticks-move_ticks>40: #40
move_ticks = gticks
move()
move_mine()
check_cannon()
if gticks-init_miss_ticks>300:
init_miss_ticks = gticks
g_pad[GAMEPAD_DELAY] = 1
if gticks-twinkle_ticks>1000:
twinkle_ticks = gticks
twinkle_stars()
if gticks-explode_ticks>10:
explode_ticks = gticks
explode()
if gticks-cannon_ticks>15:
cannon_ticks = gticks
move_cannon()
move_missile()
move_cannon_misl()
ctrl[GAME_CTL_EXIT] = 1
print('core0 done')
# ── Core 1: draw loop ─────────────────────────────────────────────────────────
@micropython.viper
def core1():
sleep_ms(500)
ctrl = ptr32(GAME_CTL)
while not ctrl[GAME_CTL_EXIT]:
draw()
print('core1 done')
def shutdown():
machine.reset()
GAME_CTL[GAME_CTL_EXIT] = 1
sleep_ms(200)
display.deinit()
machine.freq(150_000_000)
print('shutdown...')
if __name__=='__main__':
display = DVI_RP2_HSTX()
display.begin(fb, rv_colors.COLOR_MODE_BGR233,
height=SCREEN_H, width=SCREEN_W, bytes_per_pixel=1)
FLAG_ADDR = addressof(display._frame_flag)
gc.collect()
print('mem free:', gc.mem_free())
_thread.start_new_thread(core1, ())
sleep_ms(200)
try:
core0()
shutdown()
except KeyboardInterrupt:
shutdown()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment