A simple tic-80 fantasy console card in Lua scrtipting language, that display some possible effects of vbank() usage. That's an equivalent to Amiga, hardware video bitmap mixing. This kind of video mixing technology still exists on several ARM SoCs. At least seen on AllWinner and Rockchip SoCs.
Vbank are
- Two independant 16 colours palette layers
- Can be modified independantly, during TIC() main function, or even BDR() rastersync function, allowing nice effects
- the vbank(1) accept a transparent colour to display under layer through. By default it's colour 0.
It replace former OVR() "overlay" function mode. See this old demo
Table of Content
- Filling of the background
- Using vbank() to switch video banks
- Sprite trick
- Realtime fantasy hardware accelerated twist and colors effects
- The demo
press several tume ESC key, then choose close game and then press ESC key again to go to source code, and play with it. Enjoy.
This TIC-80 is also available on official tic80 website
Warning, the default, lower vbank is vbank 0, the upper bank is vbank1
Filling of the background
To laverage as much as possible TIC-80 C compiled code instead of Lua script, for heavy loads we use tilemap for filing the two vbank at each frame. More optimal way, would be to fill background the first time, and then only what changed, but even slower microcontroller can fill this fast enough to have 30fps.
Prepare the tiles map
Table of tiles are a list that are loop read sqeuentially in main TIC() function and send to map_fill() every t/n frames (where t is time and n is number of frames before changes).
-- number, sx, sy
fill_list0={
{34,2,2},
{36,2,2},
{32,2,2},
{96,4,4},
}
The map area to fill is given to map_fill function with scr parameter 2×2 screens of 30×17 tiles (of 8×8pixels = 240×136 pixels in total) are used for vbank0 and 2×2 screen starting at tile 34,0 for bank1:
tile[1]is the number of the of the up-left tiletile[2]is the number of tiles on x axistile[3]is the nimber of tiles on y axis
-- fill background tilemap
function map_fill(scr,tile)
start=scr*30*2
-- sx * 17
for y=0,33,tile[3] do
-- sy * 30
for x=0,59,tile[2] do
for j=1,tile[3] do
for i=1,tile[2] do
mset(start+x+(i-1), y+(j-1),
tile[1]+(i-1)+(j-1)*16)
end
end
end
end
end
| i | i + 1 | i + 3 |
| i + 16 | i + 17 | i + 18 |
| i + 32 | i + 33 | i + 34 |
Tiles relative to tile i of a sprite
Test to change to t/n frames:
- if t modulo 120 = 0 then we change the background
- cur1 (current screen1 tile) is incremented 1, modulo the number of elements of fill_liste1
- tile curl+1 (Lua datas tables start at 1), but the simulated hardware at 0, as on real hardware and most computing language, as you see above.
if t%120==0 then
cur1=(cur1+1)%#fill_list1
map_fill(1,fill_list1[cur1+1])
end
TIP: As can be seen with
help ramcommand in console, the memory of the tile map start at 0x08000, so it can also be set here by using thepoke()function.
Copy tile map to the screen
The map() function is then used to fill screen buffer with tile map. As for any function you can type help map to have the function call parameters. help api will show you all available TIC-80 API functions.
Lua function are
math.sin()andmath.cos()but an alias was made at top:sin=math.sin
the 60,34 (2×2 tile map screens) starting at tile 60,0 are mapped to the screen coordinate:
x = -50+50*cos(t/50)y = -50+50*sin(t/50)
so -50,-50 center on which is added the rotation of 50 pixel ray wide at a speed of t/50 (1 whole circle is 2×π ~= 6.28)
The ending 0 is for colorkey. Warning for vbank(1), this will add transparency.
map(60,0, 60,34,
-50+50*cos(t/50), -50+50*sin(t/50)
,0)
Using vbank() to switch video banks
We use about the same function on both vbank, with stlightly different parameters to have them vary a different way. The function vbank() is used to switch current drawing vbank.
As you can see, on the vbank 1, we draw a circle with color 0 (last parameter here). This what make the upper layer transparent:
-- move to vbank0 at each frame
vbank(0)
...
map(0,0,60,34,
-50-20*sin(t/20),-50+20*sin(t/20))
spr(1+t%60//30*2,x,y,14,3,0,0,2,2)
print("HELLO WORLD!",84,84)
-- pass to vbank1 for upper layer
vbank(1)
-- fill the background with map
map(60,0, 60,34,
-50+50*cos(t/50), -50+50*sin(t/50)
,0)
-- draw a circle mask with color 0 for transparency
circ(120+30*sin(t/50),50,40+20*sin(t/60),0)
Sprite trick
At the right, applying palette color swaps.
Finally we swap palette map ccolors for drawing the sprite, and have a mask of its shape too. More generally. Every kind of drawing with color 0 will allow to see the lower vbank. Video processor memory layout and registers can be see by the command help vram
At top of program, we define one time the VRAM PALETTE_MAP registers, we multiply by 2 here because we will use the 4 bits only poke4() function to write in, not the poke() 8 bits (one byte), function.
PALETTE_MAP = 0x3ff0*2
Then we remap color 8 and 10 to color 0, draw the sprite, and reset it to their own color.
-- shift blues colors of TIC sprite to
-- 0, for adding it to the mask
poke4(PALETTE_MAP+8,0)
poke4(PALETTE_MAP+10,0)
spr(1+t%60//30*2,20,80,14,3,0,0,2,2)
-- set back to normal color
poke4(PALETTE_MAP+8,8)
poke4(PALETTE_MAP+10,10)
By default, colour 0 is transparency, but it can be set to another colour by poking 0x03FF8:
poke(0x03FF8,transparent_colour_number)
Realtime fantasy hardware accelerated twist and colors effects
The function BDR(y) is called at each virtual begining of scanline. This allow you to change screen context fot the whole line. This include video pointers, video parameters etc... y is the current screen scanline number.
Warning: Scanlines are overscan vertically they start 4 lines before the line 0 of the buffer drawable screen (lines 0 to 3), and end (from 140 to 143) after it, you need to in your changes calculation. y=0 is before the 0 line
We set vbank(0) to apply to it a sinusoidal offset at each line, depending on frame time t, and current line y. 0x3ff9 is x offset register and 0x3ffa is y offset register. Only the x offset is then set for vbank(1), as you can see they can be set a different way for each vbank.
vbank(0) --standard layer
-- horizontal and vertical screen offset
poke(0x3ff9,8*sin((t+y)/10)%256)
poke(0x3ffa,8*sin((t+y)/20)%256)
vbank(1) --upper layer
-- slower but wider horizontal offset
poke(0x3ff9,5*sin((t+y)/20)%256)
Finally, we change the color palettte number 5 (middle green by defaut), to have a smooth color gradiant
-- change coloour #5
local base=0x3fc0+ 5*3
poke(base, 0,(255*y/140)//1) --R
poke(base+1,220+(255-220)*y/140)--G
poke(base+2,(255*y/140)//1) --B
The demo
- CLICK TO PLAY -