Created
May 23, 2022 16:08
-
-
Save samneggs/8780274c94c8ce3e00ef8810b3ee9bc8 to your computer and use it in GitHub Desktop.
Falling Sand and Water, single assembly routine for both threads
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gc9a01 | |
from gc9a01 import color565 | |
from machine import Pin, SPI, PWM, WDT, freq | |
import framebuf | |
from time import sleep_ms, sleep_us, ticks_diff, ticks_us, sleep | |
from micropython import const | |
import array | |
from usys import exit | |
import gc | |
from random import randint | |
from uctypes import addressof | |
import _thread | |
MAXSCREEN_X = const(240) | |
MAXSCREEN_Y = const(240) | |
WATER_COLOR = const(0xff00) | |
SAND_COLOR = const(0x00ff) | |
FIXED_COLOR = const(0xffff) | |
RANDOM_ADR = const(0x4006001c) | |
display_buffer=bytearray(MAXSCREEN_X * MAXSCREEN_Y * 2) | |
text_buffer=bytearray(8*80*2) | |
DONE=False | |
RIGHT=False | |
def bl_ctrl(duty): | |
pwm = PWM(Pin(13)) | |
pwm.freq(1000) | |
if(duty>=100): | |
pwm.duty_u16(65535) | |
else: | |
pwm.duty_u16(655*duty) | |
# http://www.penguintutor.com/programming/picodisplayanimations | |
def blit_image_file(filename,width,height): # file width, file height | |
#print(filename,gc.mem_free()) | |
with open (filename, "rb") as file: | |
file_position = 0 | |
char_position = 0 | |
current_byte = file.read(4) # header | |
while file_position < (width * height * 2): | |
current_byte = file.read(1) | |
# if eof | |
if len(current_byte) == 0: | |
break | |
# copy to buffer | |
tex_buff[char_position] = ord(current_byte) | |
char_position += 1 | |
file_position += 1 | |
file.close() | |
#screen.blit(texture,50,50) | |
#tft.blit_buffer(screen, 20, 0, MAXSCREEN_X, MAXSCREEN_Y) | |
#exit() | |
def blocks(): | |
for i in range(10): | |
dx=20 | |
min_y=120 | |
screen.fill_rect(randint(0,220),randint(min_y,220),10,10,0xffff) | |
x=randint(50,220) | |
y=randint(min_y,220) | |
screen.line(x,y,x+dx,y+dx,0xffff) | |
screen.line(x,y+1,x+dx,y+dx+1,0xffff) | |
x=randint(50,220) | |
y=randint(min_y,220) | |
screen.line(x,y,x-dx,y+dx,0xffff) | |
screen.line(x,y+1,x-dx,y+dx+1,0xffff) | |
@micropython.viper | |
def sand2(): | |
screen_addr=ptr16(display_buffer) | |
for y in range(220,0,-1): | |
for x in range(0,240): | |
source=y*MAXSCREEN_X+x-MAXSCREEN_X | |
sourcecolor=screen_addr[source] | |
if sourcecolor == 0xff00 or sourcecolor == 0: | |
continue | |
dest=y*MAXSCREEN_X+x | |
destcolor=screen_addr[dest] | |
if destcolor == 0: | |
screen_addr[source] = 0 | |
screen_addr[dest] = sourcecolor | |
elif screen_addr[source+1+MAXSCREEN_X] == 0: | |
screen_addr[source] = 0 | |
screen_addr[source+1+MAXSCREEN_X] = sourcecolor | |
elif screen_addr[source-1+MAXSCREEN_X] == 0: | |
screen_addr[source] = 0 | |
screen_addr[source-1+MAXSCREEN_X] = sourcecolor | |
SCREEN_CTL = const(0) | |
MIN_X_CTL = const(4) | |
MAX_X_CTL = const(8) | |
SAND_CTL = const(12) | |
WATER_CTL = const(16) | |
FIXED_CTL = const(20) | |
RANDOM_CTL = const(24) | |
@micropython.asm_thumb | |
def sand_asm(r0): | |
mov(r1,239) # r1 = y | |
label(LOOP_Y) | |
mov(r7,255) # stack marker | |
push({r7}) | |
mov(r7,1) | |
mov(r2,r1) | |
and_(r2,r7) | |
bne(LEFT2RIGHT) | |
ldr(r2,[r0,MAX_X_CTL]) # max x | |
b(LOOP_X) | |
label(LEFT2RIGHT) | |
ldr(r2,[r0,MIN_X_CTL]) # min x | |
label(LOOP_X) # r2 = x | |
mov(r3,240) # 240 | |
mul(r3,r1) # 240*y | |
add(r3,r3,r2) # 240*y+x | |
mov(r4,r3) | |
sub(r4,240) # -XMAX | |
add(r3,r3,r3) # double ldrh | |
ldr(r5,[r0,SCREEN_CTL]) | |
add(r3,r3,r5) # r3 = dest addr | |
add(r4,r4,r4) # double ldrh | |
add(r4,r4,r5) # r4 = source addr | |
ldrh(r5,[r4,0]) # r5 = sourcecolor | |
cmp(r5,0) | |
beq(NEXT_X) # skip if source=0 | |
ldr(r7,[r0,FIXED_CTL]) | |
cmp(r5,r7) | |
beq(NEXT_X) # skip if source fixed | |
ldrh(r6,[r3,0]) # r6=destcolor | |
cmp(r6,0) | |
beq(MOVEDOWN) # move down if dest=0 | |
ldr(r7,[r0,WATER_CTL]) | |
cmp(r7,r5) | |
beq(TRYPLUS1) # try move diag if source=water | |
cmp(r5,0xff) # 0x00ff=yellow | |
beq(YELLOW) | |
b(NEXT_X) | |
label(YELLOW) | |
cmp(r6,r7) # is dest water? | |
bne(TRYPLUS1) | |
strh(r7,[r4,0]) # source = 0 | |
strh(r5,[r3,0]) # dest = sourcecolor | |
b(NEXT_X) | |
label(MOVEDOWN) | |
mov(r6,0) | |
strh(r6,[r4,0]) # source = 0 | |
strh(r5,[r3,0]) # dest = sourcecolor | |
b(NEXT_X) | |
label(TRYPLUS1) | |
mov(r3,r4) # source | |
mov(r6,241) # 1 + MAX_X | |
add(r6,r6,r6) # double ldrh | |
add(r3,r3,r6) # source + 1 + MAX_X | |
ldrh(r6,[r3,0]) # r6=diag color | |
cmp(r6,0) | |
beq(DIAGRIGHT) # if dest empty move diag | |
cmp(r6,r7) | |
bne(TRYMINUS1) # if dest not water skip | |
cmp(r5,0xff) | |
bne(TRYMINUS1) # if source not sand skip | |
label(DIAGRIGHT) # move diag to right | |
strh(r6,[r4,0]) # source = 0 | |
strh(r5,[r3,0]) # source + 1 + MAX_X = sourcecolor | |
b(NEXT_X) | |
label(TRYMINUS1) | |
sub(r3,4) # source-1+MAXSCREEN_X | |
ldrh(r6,[r3,0]) | |
cmp(r6,0) | |
beq(DIAGLEFT) # if dest empty move diag | |
cmp(r5,r7) | |
beq(SAVE_WATER) # if source is water skip | |
cmp(r6,r7) | |
beq(DIAGLEFT) # if dest is water move diag | |
b(SAVE_WATER) | |
label(DIAGLEFT) | |
strh(r6,[r4,0]) # source = 0 | |
strh(r5,[r3,0]) # source - 2 + MAX_X = sourcecolor | |
b(NEXT_X) | |
label(SAVE_WATER) | |
cmp(r5,0xff) # is sand? | |
beq(NEXT_X) | |
mov(r7,r2) | |
push({r7}) | |
label(NEXT_X) | |
mov(r7,1) | |
mov(r6,r1) | |
and_(r6,r7) | |
bne(LEFT2RIGHT1) | |
sub(r2,1) | |
ldr(r7,[r0,MIN_X_CTL]) # min x | |
cmp(r2,r7) | |
bne(LOOP_X) | |
b(WATER) | |
label(LEFT2RIGHT1) | |
add(r2,1) | |
ldr(r7,[r0,MAX_X_CTL]) # max x | |
cmp(r2,r7) | |
bne(LOOP_X) | |
label(WATER) #------DO WATER LEFT/RIGHT---------- | |
pop({r7}) | |
cmp(r7,255) | |
beq(WATER_DONE) | |
add(r7,1) | |
mov(r4,r1) | |
sub(r4,1) | |
mov(r3,240) # 240 | |
mul(r3,r4) # 240*y | |
add(r3,r3,r7) # 240*y+x | |
sub(r3,2) # 240*y+x-2 | |
add(r3,r3,r3) # double ldrh | |
ldr(r5,[r0,SCREEN_CTL]) | |
add(r3,r3,r5) # r3 = dest addr | |
ldrh(r5,[r3,2]) # r5 = source color | |
label(RANDOM) | |
ldr(r7,[r0,RANDOM_CTL]) # get random addr | |
ldr(r7,[r7,0]) # get random bit | |
cmp(r7,1) # choose left/right first | |
beq(LEFT_FIRST) | |
b(TEST_RIGHT2) | |
label(LEFT_FIRST) | |
b(TEST_LEFT) | |
label(TEST_LEFT) | |
ldrh(r4,[r3,0]) # r4 = left dest color | |
cmp(r4,0) | |
bne(TEST_RIGHT) | |
mov(r6,0) | |
strh(r6,[r3,2]) | |
strh(r5,[r3,0]) # move color to left | |
b(WATER) | |
label(TEST_RIGHT) | |
ldrh(r4,[r3,4]) # r4 = right dest color | |
cmp(r4,0) | |
bne(WATER) | |
mov(r6,0) | |
strh(r6,[r3,2]) | |
strh(r5,[r3,4]) # move color to right | |
b(WATER) | |
label(TEST_RIGHT2) | |
ldrh(r4,[r3,4]) # r4 = right dest color | |
cmp(r4,0) | |
bne(TEST_LEFT2) | |
mov(r6,0) | |
strh(r6,[r3,2]) | |
strh(r5,[r3,4]) # move color to right | |
b(WATER) | |
label(TEST_LEFT2) | |
ldrh(r4,[r3,0]) # r4 = left dest color | |
cmp(r4,0) | |
bne(WATER) | |
mov(r6,0) | |
strh(r6,[r3,2]) | |
strh(r5,[r3,0]) # move color to left | |
b(WATER) | |
label(WATER_DONE) | |
sub(r1,1) | |
beq(EXIT) | |
b(LOOP_Y) | |
label(EXIT) | |
def core1(): | |
global DONE,screen,RIGHT,control_asm | |
i=0 | |
while i<240: | |
i+=1 | |
gticks=ticks_us() | |
if i==50: | |
screen.fill_rect(randint(0,220),0,20,20,0xff) #0x7c02) | |
if i==100: | |
screen.fill_rect(randint(0,220),0,20,20,0xff00) #0x7c02) | |
i=0 | |
screen.pixel(randint(0,240),0,0xff00) | |
screen.pixel(randint(90,95),0,0xff) | |
RIGHT=True | |
sand_asm(control_lasm) | |
#if i==25 or i==50 or i==75 or i==99: | |
tft.blit_buffer(screen, 0, 0, MAXSCREEN_X, MAXSCREEN_Y) | |
fps=1_000_000//ticks_diff(ticks_us(), gticks) | |
print(fps) | |
DONE=True | |
@micropython.viper | |
def resize(): | |
screen_addr=ptr16(display_buffer) | |
text_addr=ptr16(text_buffer) | |
offset = 80*MAXSCREEN_X+10 | |
for y in range(8): | |
for x in range(60): | |
color=text_addr[y*80+x] | |
for ly in range(5): | |
for lx in range(5): | |
screen_addr[(ly+(5*y))*MAXSCREEN_X+(4*x)+lx+offset]=color | |
if __name__=='__main__': | |
spi = SPI(1, baudrate=80_000_000, sck=Pin(10), mosi=Pin(11)) | |
tft = gc9a01.GC9A01( | |
spi, | |
240, | |
240, | |
reset=Pin(12, Pin.OUT), | |
cs=Pin(9, Pin.OUT), | |
dc=Pin(8, Pin.OUT), | |
backlight=Pin(13, Pin.OUT), | |
rotation=0) | |
tft.init() | |
tft.rotation(0) | |
tft.fill(gc9a01.BLACK) | |
tft.inversion_mode(True) | |
bl_ctrl(100) | |
sleep(0.5) | |
screen=framebuf.FrameBuffer(display_buffer, MAXSCREEN_X , MAXSCREEN_Y, framebuf.RGB565) | |
txt=framebuf.FrameBuffer(text_buffer, 80 , 8, framebuf.RGB565) | |
control_lasm=array.array('I',(addressof(screen),0,120,SAND_COLOR,WATER_COLOR,FIXED_COLOR,RANDOM_ADR)) | |
control_rasm=array.array('I',(addressof(screen),120,240,SAND_COLOR,WATER_COLOR,FIXED_COLOR,RANDOM_ADR)) | |
txt.text('PI PICO',0,0,0xffff) | |
resize() | |
#blit_image_file('floor.bin',32,32*8) # width, height | |
gticks=ticks_us() | |
#wdt = WDT(timeout=8300) # Watchdog timer reset | |
#blocks() | |
#tft.blit_buffer(screen, 0, 0, MAXSCREEN_X, MAXSCREEN_Y) | |
screen.fill_rect(0,200,239,239,0xff00) | |
#screen.line(0,50,240,50,0xffff) | |
_thread.start_new_thread(core1, ()) | |
while not DONE: | |
if RIGHT: | |
RIGHT=False | |
sand_asm(control_rasm) | |
exit() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Amazing example of MicroPython optimization! Love it