Skip to content

Code examples and snippets

borb.lua edited this page Feb 5, 2019 · 58 revisions
  1. Load string palette
  2. Inc and Dec
  3. Glitch
  4. Palette swapping
  5. Screen wave and sky gradient
  6. Pal function
  7. Shake screen
  8. Trace all global variables
  9. Simple Sprite Flags
  10. Set/Get spritesheet pixel
  11. Draw circle function
  12. Draw ellipse function on Moonscript
  13. Draw Bézier curves on Moonscript
  14. peek4 and poke4
  15. BrainF**k on Moonscript
  16. Some code snippets made by Raidez (contains useful class for: collision (rectangle and circle), sprite, menu)
  17. Perlin Noise
  18. Write and read function from memory
  19. Mute one channel of a music track

Load string palette

Useable with palette string defined in palette page

--Load palette string
PAL_ARNE16="0000001b2632005784493c2ba4642244891abe26332f484e31a2f2eb89319d9d9da3ce27e06f8bb2dceff7e26bffffff"
for i=0,15 do
 r=tonumber(string.sub(PAL_ARNE16,i*6+1,i*6+2),16)
 g=tonumber(string.sub(PAL_ARNE16,i*6+3,i*6+4),16)
 b=tonumber(string.sub(PAL_ARNE16,i*6+5,i*6+6),16)
 poke(0x3FC0+(i*3)+0,r)
 poke(0x3FC0+(i*3)+1,g)
 poke(0x3FC0+(i*3)+2,b)
end
function loadPalette(string){
	for(var i=0;i<16;i++){
		poke(0x03FC0+(i*3),parseInt(string.substr(i*6,2),16));
		poke(0x03FC0+(i*3)+1,parseInt(string.substr((i*6)+2,2),16));
		poke(0x03FC0+(i*3)+2,parseInt(string.substr((i*6)+4,2),16));
	}
}

Inc and Dec

These functions increment and decrement variables by 1. Works only for globals. The use is for minifying the code when you have many somevar = somevar+1 calls which can waste many characters when names are very descriptive such as enemy_health, while still keeping the code readable.

function inc(v)
 _G[v] = _G[v] + 1
end
function dec(v)
 _G[v] = _G[v] - 1
end

With MoonScript you can use the += and -= operators.

Glitch

-- 'Change screen offset in every scanline' demo
-- author: Vadim
shake=0
d=4
function TIC()
	if btnp()~=0 then shake=30 end
	if shake>0 then
		poke(0x3FF9+1,math.random(-d,d))
		shake=shake-1		
		if shake==0 then memset(0x3FF9,0,2) end
	end
	cls(12)
	print("PRESS ANY KEY TO GLITCH!",54,64)
end

function scanline(row)
	if shake>0 then
		poke(0x3FF9,math.random(-d,d))		
	end
end

Palette swapping

-- swap c0 and c1 colors, call pal() to reset
function pal(c0,c1)
	if(c0==nil and c1==nil)then for i=0,15 do poke4(0x3FF0*2+i,i)end
	else poke4(0x3FF0*2+c0,c1)end
end
function pal(c0,c1){
	if(c0===undefined&&c1===undefined){
		for(var i=0;i<16;i++){poke4(32736+i,i);}
	}else{
		poke4(32736+c0,c1);
	}
}

Screen wave and sky gradient

-- title: screen wave and sky gradient snippet
-- author: Darkhog

-- TIC function cut because it doesn't matter here.
-- This code can be used for e.g. water or some hazy/hot environments.
wavelimit = 136/2
function scanline(row)
	-- skygradient
	poke(0x3fc0,190-row)
	poke(0x3fc1,140-row)
	poke(0x3fc2,0)
	-- screen wave
	if row>wavelimit then
		poke(0x3ff9,math.sin((time()/200+row/5))*10)
	else
		poke(0x3ff9,0)
	end
end

pal function

--sets the palette indice i to specified rgb
--or return the colors if no rgb values are declared.
function pal(i,r,g,b)
	--sanity checks
	if i<0 then i=0 end
	if i>15 then i=15 end
	--returning color r,g,b of the color
	if r==nil and g==nil and b==nil then
		return peek(0x3fc0+(i*3)),peek(0x3fc0+(i*3)+1),peek(0x3fc0+(i*3)+2)
	else
		if r==nil or r<0 then r=0 end
		if g==nil or g<0 then g=0 end
		if b==nil or b<0 then b=0 end
		if r>255 then r=255 end
		if g>255 then g=255 end
		if b>255 then b=255 end
		poke(0x3fc0+(i*3)+2,b)
		poke(0x3fc0+(i*3)+1,g)
		poke(0x3fc0+(i*3),r)
	end
end
function pal(i,r,g,b){
	i=(i|0)*3;
	if(r===undefined&&g===undefined&&b===undefined){
		return[peek(16320+i),peek(16321+i),peek(16322+i)];
	}else{
		poke(16320+i,r);poke(16321+i,g);poke(16322+i,b);
	}
}

Shake screen

-- 'shake screen demo' demo
-- author: Vadim

shake=0
d=4
function TIC()
	if btnp()~=0 then shake=30 end
	if shake>0 then
		poke(0x3FF9,math.random(-d,d))
		poke(0x3FF9+1,math.random(-d,d))
		shake=shake-1		
		if shake==0 then memset(0x3FF9,0,2) end
	end
	cls(12)
	print("PRESS ANY KEY TO SHAKE THE SCREEN!",24,64)
end

Trace all global variables by Al Rado

-- title:  Trace all global variables
-- author: Al Rado 28.02.2017
-- desc:   Standard Lua demo
-- script: lua
-- input:  gamepad
-- pal:    DB16

local seen={}

function dump(t,i)
    seen[t]=true
    local s={}
    local n=0
    for k in pairs(t) do
        n=n+1 s[n]=k
    end
    table.sort(s)
    for k,v in ipairs(s) do
        trace(i..v)
        v=t[v]
        if type(v)=="table" and not seen[v] then
            dump(v,i.."\t")
        end
    end
end

cls()
trace("---------------------")
trace("All global variables:")
dump(_G,"")
trace("---------------------")

print("See all global variables in console!")

function TIC() end

Simple Sprite Flags

FLAGS={}
function fset(i,f,v)
  local val=FLAGS[i] or 0
  local mask=1<<f
  FLAGS[i]=v 
    and val|mask 
    or val&~mask
end

function fget(i,f)
  local val=FLAGS[i] or 0
  return val&(1<<f)~=0
end

fset(5,5,true)
trace(fget(5,5))
fset(5,5,false)
trace(fget(5,5))

cls()

function TIC()
end

Set/Get spritesheet pixel

The spritesheet isn't a 128x128 image in the memory, it's 256 consecutive 8x8 4 bit sprites

-- set spritesheet pixel
function sset(x,y,c)
    local addr=0x4000+(x//8+y//8*16)*32 -- get sprite address
    poke4(addr*2+x%8+y%8*8,c) -- set sprite pixel
end
-- get spritesheet pixel
function sget(x,y)
    local addr=0x4000+(x//8+y//8*16)*32 -- get sprite address
    return peek4(addr*2+x%8+y%8*8) -- get sprite pixel
end
function sget(x,y){
	return peek4(32768+56*(x/8|0)+960*(y/8|0)+x+8*(y|0));
}
function sset(x,y,c){
	poke4(32768+56*(x/8|0)+960*(y/8|0)+x+8*(y|0),c);
}

Draw circle function

made by Raidez

function _360(x,y,r,color,fill)
 if not fill then
  for a=1,360,1 do --draw outline only
   dx=math.cos(a)*r+x
   dy=math.sin(a)*r+y
   pix(dx,dy,color)
  end
 else
  for r=r,1,-1 do --draw circle for each radius (for fill circle)
   for a=1,360,1 do
    dx=math.cos(a)*r+x
    dy=math.sin(a)*r+y
    pix(dx,dy,color)
   end
  end
 end
end


function _bresenham(x,y,r,color)
 px=0
 py=r
 m=5-4*r
 
 while(px<=py) do
  pix((px+x),(py+y),color) 
  pix((py+x),(px+y),color)
  pix((-px+x),(py+y),color)
  pix((-py+x),(px+y),color)
  pix((px+x),(-py+y),color)
  pix((py+x),(-px+y),color)
  pix((-px+x),(-py+y),color)
  pix((-py+x),(-px+y),color)
 
  if m>0 then
   py=py-1
   m=m-8*py
  end
 
  px=px+1
  m=m+8*px+4
 end
end


function _andres(x,y,r,color,fill)
 px=x
 py=y
 
 d=r-1
 a=r
 --a=r-1
 b=0
 
 while(a>=b) do
  if not fill then
   pix((px+b),(py+a),color)
   pix((px+a),(py+b),color)
   pix((px-b),(py+a),color)
   pix((px-a),(py+b),color)
   pix((px+b),(py-a),color)
   pix((px+a),(py-b),color)
   pix((px-b),(py-a),color)
   pix((px-a),(py-b),color)
  else
   line((px+b),(py-a),(px+b),(py+a),color)
   line((px+a),(py-b),(px+a),(py+b),color)
   line((px-b),(py-a),(px-b),(py+a),color)
   line((px-a),(py-b),(px-a),(py+b),color)
  end
 
  if d>=2*b then
   d=d-2*b-1
   b=b+1
  elseif d<2*(r-a) then
   d=d+2*a-1
   a=a-1
  else
   d=d+2*(a-b-1)
   a=a-1
   b=b+1
  end
 end
end


function _axis(cx,cy,r,color,fill)
 if not fill then
  for y=-r,r,1 do --outline
   for x=-r,r,1 do
    if (x*x+y*y)<=(r*r) then
     pix(cx+x,cy+y,color)
    end
   end
  end
 
  r=r-1
  for y=-r,r,1 do --unfill
   for x=-r,r,1 do
    if (x*x+y*y)<=(r*r) then
     pix(cx+x,cy+y,-1)
    end
   end
  end
 else
  for y=-r,r,1 do
   for x=-r,r,1 do
    if (x*x+y*y)<=(r*r) then
     pix(cx+x,cy+y,color)
    end
   end
  end
 end
end


function circ(x,y,r,color,algo)
 if algo=="360" then
  --don't use it, it's false/incomplete
  --_360(x,y,r,color,true)
 elseif algo=="bresenham" then
  _bresenham(x,y,r,color)
  _andres(x,y,(r),color,true)
 elseif algo=="andres" then
  _andres(x,y,r,color,true)
 elseif algo=="axis" then
  _axis(x,y,r,color,true)
 end
end


function circb(x,y,r,color,algo)
 if algo=="360" then
  --don't use it, it's false/incomplete
  --_360(x,y,r,color,false)
 elseif algo=="bresenham" then
  _bresenham(x,y,r,color)
 elseif algo=="andres" then
  _andres(x,y,r,color,false)
 elseif algo=="axis" then
  _axis(x,y,r,color,false)
 end
end 

function TIC()
	cls()
	
	circb(100,60,40,14,"bresenham")
end

Draw ellipse function on Moonscript

made by harraps

-- script: moon
--function to draw an ellipse
--x,y : position of the ellipse
--A,B : major and minor radius
--ang : angle of the ellipse in radian
--col : color of the filled ellipse
ell=(x,y,A,B,ang,col)->
	--angles used
	cos=math.cos ang
	sin=math.sin ang
	cos2=math.cos ang+math.pi*0.5
	sin2=math.sin ang+math.pi*0.5
	--vectors in euclidian space
	ux=A*cos
	uy=A*sin
	vx=B*cos2
	vy=B*sin2
	--bounds of ellipse
	w=math.sqrt ux*ux + vx*vx
	h=math.sqrt uy*uy + vy*vy
	--rect x-w+1,y-h+1,w*2,h*2,8
	--for each pixel within the bounds
	for  x0=-w,w
		for y0=-h,h
			x1=( cos*x0 + sin*y0)/A
			y1=(-sin*x0 + cos*y0)/B
			if x1*x1 + y1*y1 < 1
				pix x+x0,y+y0,col

Draw Bézier curves on Moonscript

made by harraps

-- title:  bezier
-- desc:   function to draw Bezier curves
-- script: moon
-- input:  gamepad

class Point
	new:(x,y)=>
		@x=x
		@y=y

Bezier =
	-- simple line
	linear:(t,p0,p1)->
		return{
			x:p0.x+t*(p1.x-p0.x)
			y:p0.y+t*(p1.y-p0.y)
			--z:p0.z+t*(p1.z-p0.z)
		}
	-- simple curve
	quadratic:(t,p0,p1,p2)->
		n =1-t
		t0=n*n
		t1=n*t*2
		t2=t*t
		return{
			x:t0*p0.x+t1*p1.x+t2*p2.x
			y:t0*p0.y+t1*p1.y+t2*p2.y
			--z:t0*p0.z+t1*p1.z+t2*p2.z
		}
	--better curve
	cubic:(t,p0,p1,p2,p3)->
		n =1-t
		t0=n*n*n
		t1=3*n*n*t
		t2=3*n*t*t
		t3=t*t*t
		return{
			x:t0*p0.x+t1*p1.x+t2*p2.x+t3*p3.x
			y:t0*p0.y+t1*p1.y+t2*p2.y+t3*p3.y
			--z:t0*p0.z+t1*p1.z+t2*p2.z+t3*p3.z
		}


class Linear
	
	new:(col,seg,x0,y0,x1,y1)=>
		@col=col
		@seg=seg
		@p0 =Point x0,y0
		@p1 =Point x1,y1
		
	draw:=>
		t=0
		pad=1/@seg
		pp=@p0
		for i=1,@seg
			t+=pad
			np=Bezier.linear t,@p0,@p1
			line pp.x,pp.y,np.x,np.y,@col
			pp=np
		line pp.x,pp.y,@p1.x,@p1.y,@col


class Quadratic
	
	new:(col,seg,x0,y0,x1,y1,x2,y2)=>
		@col=col
		@seg=seg
		@p0 =Point x0,y0
		@p1 =Point x1,y1
		@p2 =Point x2,y2
		
	draw:=>
		t=0
		pad=1/@seg
		pp=@p0 
		for i=1,@seg
			t+=pad
			np=Bezier.quadratic t,@p0,@p1,@p2
			line pp.x,pp.y,np.x,np.y,@col
			pp=np
		line pp.x,pp.y,@p2.x,@p2.y,@col


class Cubic
	
	new:(col,seg,x0,y0,x1,y1,x2,y2,x3,y3)=>
		@col=col
		@seg=seg
		@p0 =Point x0,y0
		@p1 =Point x1,y1
		@p2 =Point x2,y2
		@p3 =Point x3,y3
		
	draw:=>
		t=0
		pad=1/@seg
		pp=@p0 
		for i=1,@seg
			t+=pad
			np=Bezier.cubic t,@p0,@p1,@p2,@p3
			line pp.x,pp.y,np.x,np.y,@col
			pp=np
		line pp.x,pp.y,@p3.x,@p3.y,@col

c=Cubic 6,60, 0,0, 246,0, 0,136, 246,136

cls 1
c\draw!

export TIC=->

peek4 and poke4

Also see this.

// script: js
function peek4(address){
	var value=peek(address>>1);
	return address&1?value>>4:value&0x0f;
}
function poke4(address,value){
	var tmp=peek(address>>1);
	value&=0x0f;
	poke(address>>1,address&1?(tmp&0x0f)|(value<<4):(tmp&0xf0)|value);
}
-- script: lua
function peek4(address)
	local value=peek(address//2)
	if(address&1)then
		return value>>4
	else
		return value&0x0f
	end
end
function poke4(address,value)
	local tmp=peek(address//2)
	value=value&0x0f
	if(address&1)then
		tmp=(tmp&0x0f)|(value<<4)
	else
		tmp=(tmp&0xf0)|value
	end
	poke(address//2,tmp)
end

BrainF**k on Moonscript

made by harraps

-- script: moon
-- title:  brainfuck interpreter
-- author: by oSchyns
-- version 0.3.0


BF="++++ ++++"..
"[>+++++ +++++<-]"..
">++++.----- ----- -.--- ---."--..
--uncomment to test error output
--"<< force a tape outbound"


IN=""


--tape to perform computation
class Tape

    new:(l,m)=>
        @l=l  --cell limit
        @m=m  --loop the finite tape
        @t={} --tape
        @h=0  --read head
        --correct values
        @l-=1 if @l~=nil
        @m-=1 if @m~=nil

    --rhead out of the bounds of the tape
    unbound:=>
        if @m~=nil then return @h>@m
        @h<0

    --init nil cell to zero
    init:=> @t[@h]=0 if @t[@h]==nil

    --move read head to the left
    mvL:=>
        @h-=1
        if @m~=nil then if @h<0
            @h=@m

    --move read head to the right
    mvR:=>
        @h+=1
        if @m~=nil then if @h>@m
            @h=0

    --decreament cell
    decr:=>
        @init!
        @t[@h]-=1
        if @l~=nil then if @t[@h]<0
            @t[@h]=@l

    --increament cell
    incr:=>
        @init!
        @t[@h]+=1
        if @l~=nil then if @t[@h]>@l
            @t[@h]=0

    --ins: insert value in cell
    --out: get value from cell

    ins:(i)=> @t[@h]=i if i~=nil
    out:   => @t[@h]   or 0


--stack to hold indexes of brackets
class Stack
    new:    => @s={}
    top:    => @s[#@s]
    pop:    => @s[#@s]=nil
    push:(i)=> @s[#@s+1]=i




--automaton to execute the program
class Automaton

    new:(p,i,l,m)=>
        @t=Tape l,m --tape
        @s=Stack!   --stack
        @r=1        --read head
        @p=""       --program
        @i=i        --input
        @o=""       --output
        @e=nil      --error
        --strip useless chars of program
        for c in p\gmatch "[<>%+%-%[%]%.%,]+"
            @p..=c
        b=@check!
        if b~=nil
            @e="brackets mismatch at "..b

    --check for brackets mismatch
    check:=>
        s=Stack!
        for i=1,#@p
            if     @program(i)=='[' then s\push i
            elseif @program(i)==']'
                return i    if #s.s<=0
                s\pop!
        return s\top! if #s.s> 0 
        return nil

    --get char from input
    input:=>
        if @i==nil or @i=="" then return nil
        c1=@i\byte! --1st char of i
        @i=@i\sub 2 --remove 1st char
        return c1

    --output:   add char to output
    --program:  get instruction at 'n'
    --continue: continue execution

    output: (n)=> @o..=string.char n
    program:(n)=> @p\sub n,n
    continue:  => @r<=#@p and @e==nil

    --find matching bracket
    match:(b)=>
        m=1
        while m>0 and b<=#@p
            b+=1
            if     @program(b)=='[' then m+=1
            elseif @program(b)==']' then m-=1
        return b

    --opening bracket
    open:=>
     --jump to matching bracket
        if  (@t\out!)==0 then @r=@match @r
        else @s\push @r

    --closing bracket
    clos:=>
        if    (@t\out!)==0   then    @s\pop!
        elseif @s\top! ~=nil then @r=@s\top!

    --automaton's' execution step 
    step:=>
        switch @program @r
            when '<' then @t\mvL!
            when '>' then @t\mvR!
            when '-' then @t\decr!
            when '+' then @t\incr!
            when ',' then @t\ins  @input!
            when '.' then @output @t\out!
            when '[' then @open!
            when ']' then @clos!
        @r+=1
        if @t\unbound!
            @e="tape outbound !"




--display to see the execution
class Display

    new:(a,s=1,f=nil)=>
        @a=a    --automaton
        @s=s/60 --number of update per second
        @c=0    --counter
        @f=3    --display format of cells
        if     f=="hexadecimal" then @f=2
        elseif f=="character"   then @f=1

    --execute automaton all at once
    execute:=>
        while @a\continue!
            @a\step!
        @output!

    --update automaton at given frame rate
    update:=>
        if @a\continue!
            if @c<1 then @c+=@s
            else
                @c=0
                @a\step!
                @draw!

    --draw automaton state and output
    draw:=>
        cls!
        @tape    8, 2
        @program 8,12
        @output 4


    --draw the tape
    tape:(w=6,h=0,c=1,a=3)=>
        rect 0,h,240,w,c
        l=math.ceil (40/@f)*0.5
        p=(w-6)*0.5
        q=6*@f
        b=l-@a.t.h
        d=15
        if     @f==2 then d=6
        elseif @f==1 then d=3
        rect l*q-d,h,6*@f,w,a
        for i=@a.t.h-l,@a.t.h+l
            unless i<0
                c=15
                c=14 if i==@a.t.h
                v=@a.t.t[i] or 0
                if     @f==3 then v="%3d"\format v
                elseif @f==2 then v="%2x"\format v
                elseif @f==1 then v=string.char  v
                print v,(i+b)*q-d,h+p,c

    --draw the program
    program:(w=6,h=6,c=1,a=3)=>
        rect   0,h,240,w,c
        rect 117,h,  6,w,a
        p=(w-6)*0.5
        b=20-@a.r
        for i=@a.r-20,@a.r+20
            unless i<1
                c=15
                c=14 if i==@a.r
                v=@a\program i
                print v,(i+b)*6-2,h+p,c

    --print result of execution
    output:(s=0)=>
        i=s
        o=@a.o.."\n"
        --print each line
        for l in o\gmatch "[%S \t]*\n"
            print l,0,i*6
            i+=1
            break if i==21 --no more room
        --print error
        unless @a.e==nil
            print @a.e,0,130,6


--create automaton 'TM' using
--program : BF
--input   : IN
TM=Automaton BF,IN,128,nil


--create display 'DP' using
--automaton    : TM
--frame rate   : 30
--cell display : decimal
DP=Display TM,30,"decimal"


--execute the automaton and display it 
export TIC=->
    DP\update!


--http://moonscript.org/reference/
--http://moonscript.org/compiler/
--https://esolangs.org/wiki/Brainfuck
--http://zacstewart.com/2013/09/15/learning-cpp-a-brainfuck-interpreter.html

Write and read function from memory

-- title:  Write function to memory
-- author: Al Rado
-- desc:   Shows how write code to memory
-- script: lua

-- addres to write code block / sprites addres
ADDR = 0x4000

-- target function, for example
function prints(msg,x,y,c)
	for i=-1,1 do
		for j=-1,1 do
			print(msg,x+i,y+j,0);
		end
	end
	print(msg,x,y,c);
end

-- write dump of target function to memory
local funcBin = string.dump(prints, true) 
for i=1,#funcBin do 
  local code = funcBin:byte(i) 
  poke(ADDR+i-1,code) 
end 
sync(0,0,true)

-- trace lenght of memory block
trace("len: "..#funcBin) 
exit()

function TIC() end 

After the function is written to the cartridge memory, it can be read and run. It is important to specify the length of the code block (LEN) shown in the previous example

-- title:  Read function from memory
-- author: Al Rado
-- desc:   Shows how read code from memory
-- script: lua

-- addres of code block / sprites addres
ADDR = 0x4000

-- lenght of memory block in bytes
LEN = 211

-- get dump of function from memory
local funcStr=""
for i=1,LEN do
  local code = peek(ADDR+i-1)   
  funcStr=funcStr..string.char(code) 
end

-- load function from string
local prints = assert(load(funcStr));

-- call target function
cls(3) 
prints("HELLO WORLD!",84,84,15) 

function TIC() end

Mute one channel of a music track

The 6-bit structure of the music tracks makes muting a single channel tricky. A channel consists of 16 patterns, and one 6-bit pattern is always span across two nibbles (a half-byte) that are from separate bytes. The other nibble also contains data from some other pattern, which should be left intact. To mute all patterns of the channel in a song, some bitmasking is needed.

-- title:  Mute one channel of a music track
-- author: @oggborbis
-- script: lua

function mutechannel(track, channel) --tracks 0-8, channels 1-4
    local offset=3*(channel+channel%2)/2-2-channel%2
    local mask
    if channel%2==0 then
        mask=0x3 --the high nibble is bitmasked empty for channels 2&4
    else
        mask=0xc --the low nibble is bitmasked empty for channels 1&3
    end
    for i=0,15 do --there are 16 sections in a track, each one taking 3 bytes for four 6-bit patterns
        local adr=(0x13e64+51*track+3*i)*2 --a track is 51 bytes long
		local q=adr+offset+channel%2
		poke4(q,peek4(q)&mask) --one half of this nibble (2bit) is bitmasked empty
		poke4(adr+offset+1-channel%2,0) --the other nibble (4bit) can be emptied
	end
end```
You can’t perform that action at this time.