This tutorial builds applications step by step. Use the language reference for the complete instruction list. The full Tetris source is in examples/tetris.fuk. A smaller typed-values example is in examples/types-and-branches.fuk.
FUK1 is a small interpreted language. It is closer to a simple assembly language than to C or Python:
- one instruction per line;
- dynamically typed integer, float, and string variables;
- execution normally moves from top to bottom;
- labels and jumps implement branches and loops;
callandreturnprovide basic reusable routines;- a 4,096-element integer array stores larger data sets;
- terminal, keyboard, random, and timing operations are exposed through VM instructions.
Applications are ordinary text files loaded at runtime. Adding or editing a .fuk file does not require rebuilding the interpreter.
Create hello.fuk as ASCII or UTF-8 without a BOM:
FUK1
println Hello from portable FUK1!
exit
Build the interpreter and run the file:
make
./fuk hello.fukThe first line must be exactly FUK1. Blank lines are allowed after it. A full-line comment starts with #.
# This comment explains why the next instruction exists.
println Ready.
Inline comments are supported outside quoted strings:
set lives 3 # player starts with three lives
str tag "#ready" # the first # is part of the string
A destination operand creates a variable if it does not exist:
set score 0
set lives 3
mov backup score
FUK1 supports 256 global variables. Names are case-sensitive, may contain letters, digits, and _, and may be up to 47 characters long. A variable can hold an integer, a float, or a string; assigning another type replaces its previous value.
A value operand can be a decimal integer or an existing variable:
set width 10
set height 20
mul area width height
Use inc and dec for counters:
inc score
dec lives
Create floats and strings with type-specific assignments:
float speed 2.5
str name dan
str full_name "Dan Smith"
Quoted strings may contain spaces. String values are limited to 127 bytes.
Text output:
print Loading...
println Done
println
Number output:
set score 125
print Score:
printv score
println
Single-character output:
putc 65
println
putc 65 prints A.
Useful text escapes:
print first\nsecond
print left\tright
print \s
print \\
\s is useful when a trailing space would otherwise be trimmed from a source line.
Read an integer:
input age Enter your age:
print Age:
printv age
println
Read the first character code:
inputc answer Continue (y/n):
if_eq answer 121 accepted
println Cancelled
exit
label accepted
println Accepted
exit
input and inputc block until Enter is pressed. Games should use key_poll instead.
Binary arithmetic uses operation destination left right:
add sum a b
sub difference a b
mul area width height
div half value 2
mod digit value 10
min lower a b
max higher a b
Unary arithmetic:
neg opposite value
abs magnitude value
Division is signed integer division. Division or remainder by zero stops the application with a runtime error.
FUK1
input a First number:
inputc op Operation (+ - * / %):
input b Second number:
if_eq op 43 do_add
if_eq op 45 do_sub
if_eq op 42 do_mul
if_eq op 47 do_div
if_eq op 37 do_mod
println Unknown operation
exit
label do_add
add result a b
goto show
label do_sub
sub result a b
goto show
label do_mul
mul result a b
goto show
label do_div
div result a b
goto show
label do_mod
mod result a b
label show
print Result:
printv result
println
exit
Read and compare a name:
FUK1
inputs name Your name:
if_seq name dan success exit
label success
printsn Welcome, dan!
exit
The last operand is the false target. It is optional for backward compatibility, and exit is a reserved branch target. The same true/false form works for integer, float, string, and Boolean branches.
String tools:
str first "Dan"
str space " "
str last "Smith"
strcat full first space
strcat full full last
strlen size full
strcmp order first last
printsn full
Float arithmetic and comparison:
float price 19.95
float tax_rate 0.05
fmul tax price tax_rate
fadd total price tax
printfn total
if_fnear total 20.9475 0.0001 correct wrong
Use if_fnear for calculated floats. Exact if_feq is useful for values that were assigned directly, but normal floating-point rounding rules still apply.
Conversions and a convenience helper:
stoi count "42"
stof ratio "0.75"
itof count_float count
ftoi whole ratio
clamp safe_x x 0 79
Conditional jumps compare two values and jump when the condition is true:
if_eq a b equal
if_ne a b different
if_lt a b less
if_le a b less_or_equal
if_gt a b greater
if_ge a b greater_or_equal
Example:
FUK1
input number Enter a number:
if_gt number 0 positive
if_lt number 0 negative
println Zero
exit
label positive
println Positive
exit
label negative
println Negative
exit
Comparison instructions store a Boolean result instead of jumping:
cmp_ge can_enter age 18
printv can_enter
println
The result is 1 for true and 0 for false.
FUK1 builds loops from labels and jumps:
FUK1
set i 1
label loop
printv i
println
inc i
if_le i 10 loop
exit
Always give a real-time loop a sleep; otherwise it can consume all available CPU time:
label frame
sleep 50
# Update state and draw here.
goto frame
call jumps to a label and remembers where execution should resume. return resumes after the latest call.
FUK1
set value 21
call print_double
println
set value 50
call print_double
println
exit
label print_double
mul result value 2
printv result
return
Variables are global. Pass values by assigning agreed variable names before call, and read results from agreed variables after it. The call stack supports 64 nested calls.
Do not enter a function by normal fall-through. Place exit or goto before function definitions when necessary.
FUK1 supports integer bit manipulation:
bit_and masked value 255
bit_or flags flags 4
bit_xor toggled flags 1
bit_not inverted value
shl doubled value 1
shr halved value 1
Shift counts must be between 0 and 31. shr is a logical right shift.
Each launch receives 4,096 signed integer cells initialized to zero.
array_set 0 42
array_get value 0
printv value
Use variables as indices:
set index 100
array_set index 7
array_get value index
Clear or initialize all cells:
array_fill 0
array_size capacity
For a grid with width 10, convert (x, y) to a linear index:
mul index y 10
add index index x
array_set index 1
Read it later:
mul index y 10
add index index x
array_get cell index
A 10×20 Tetris board uses cells 0..199; the remaining array remains available for other state.
Clear the screen:
clear
Place the next output at a zero-based character coordinate:
cursor 10 5
print @
Select a color:
color 11
println Yellow
color 15
Query terminal dimensions instead of assuming a fixed resolution:
screen_cols width
screen_rows height
sub center_x width 1
div center_x center_x 2
sub center_y height 1
div center_y center_y 2
cursor center_x center_y
print @
cursor x y
print \s
inc x
cursor x y
print @
This avoids flicker and unnecessary redraws.
key_poll returns immediately:
key_poll key
if_eq key 0 no_event
if_eq key 113 quit
if_eq key 130 move_left
if_eq key 131 move_right
Important codes:
| Key | Code |
|---|---|
| Up | 128 |
| Down | 129 |
| Left | 130 |
| Right | 131 |
| Escape | 27 |
| Enter | 10 |
q |
113 |
Inclusive random range:
rand die 1 6
rand piece 0 6
Delay execution:
sleep 50
Read a millisecond counter:
time_ms now
The counter wraps because variables are signed 32-bit integers. Use it for short elapsed-time measurements, not calendar time.
FUK1
clear
screen_cols width
screen_rows height
div x width 2
div y height 2
color 11
cursor x y
print @
label frame
sleep 40
key_poll key
if_eq key 113 quit
if_eq key 27 quit
if_eq key 130 left
if_eq key 131 right
goto frame
label erase
cursor x y
print \s
return
label left
call erase
if_le x 0 frame
dec x
cursor x y
print @
goto frame
label right
call erase
sub limit width 1
if_ge x limit frame
inc x
cursor x y
print @
goto frame
label quit
clear
exit
This example introduces screen queries, a frame delay, non-blocking input, a reusable erase routine, bounds checks, and partial redraw.
The complete source is examples/tetris.fuk. Run it with ./fuk examples/tetris.fuk.
The 10×20 board occupies 200 array cells. Cell index is y * 10 + x.
mul idx y 10
add idx idx x
array_get cell idx
Four (x, y) pairs hold the active blocks:
set ax 0
set ay 0
set bx 0
set by 0
set cx 0
set cy 0
set dx 0
set dy 0
Candidate coordinates are calculated separately. The current coordinates are updated only after every candidate block passes bounds and collision checks.
rand piece 0 6
if_eq piece 0 spawn_i
if_eq piece 1 spawn_o
if_eq piece 2 spawn_t
mul idx nay 10
add idx idx nax
array_get cell idx
if_ne cell 0 lock
mul idx ay 10
add idx idx ax
array_set idx 1
The same operation is applied to all four blocks.
set col 0
set count 0
label scan_col
if_ge col 10 scan_done
mul idx row 10
add idx idx col
array_get cell idx
if_eq cell 0 next_col
inc count
label next_col
inc col
goto scan_col
label scan_done
if_eq count 10 remove_row
The full example then shifts every row above the completed row down by one.
Start with a minimal program and add one feature at a time. Temporary output is the simplest debugger:
print x=
printv x
print y=
printv y
println
Common failures:
- typo in an instruction or label;
- variable read before creation;
- index outside
0..4095; - cursor outside the current terminal dimensions;
- division by zero;
returnwithout a matchingcall;- more than 64 nested calls;
- source saved with a BOM;
- application written for a newer VM than the kernel on the drive.
- Write the application state as a list of variables and array regions.
- Keep variable names meaningful; 256 are available, but clear state design still matters.
- Query screen dimensions for portable terminal layouts.
- Validate array indices and coordinates before use.
- Add
sleepto every continuous real-time loop. - Calculate candidate state before committing movement.
- Use
callfor repeated routines that have one clear entry and return point. - Add temporary
printvdiagnostics around failing branches. - Test error paths, not only the successful path.
- Run the finished file with
./fuk path/to/program.fukand test both success and error paths.
FUK1
print text
println text
printv value
printf value
printfn value
prints value
printsn value
putc value
input dst prompt
inputc dst prompt
inputf dst prompt
inputs dst prompt
key_poll dst
set dst value
mov dst value
float dst value
fset dst value
str dst value
sset dst value
inc variable
dec variable
neg dst value
abs dst value
add dst a b
sub dst a b
mul dst a b
div dst a b
mod dst a b
min dst a b
max dst a b
clamp dst value low high
fneg dst value
fabs dst value
fadd dst a b
fsub dst a b
fmul dst a b
fdiv dst a b
fmin dst a b
fmax dst a b
strlen dst value
strcmp dst a b
strcat dst a b
stoi dst string
stof dst string
itof dst integer
ftoi dst float
bit_not dst value
bit_and dst a b
bit_or dst a b
bit_xor dst a b
shl dst value bits
shr dst value bits
array_set index value
array_get dst index
array_fill value
array_size dst
clear
cursor x y
color id
screen_cols dst
screen_rows dst
sleep milliseconds
time_ms dst
rand dst min max
label name
goto name
call name
return
if_eq a b label
if_ne a b label
if_lt a b label
if_le a b label
if_gt a b label
if_ge a b true_label [false_label]
if_feq a b true_label [false_label]
if_fne a b true_label [false_label]
if_flt a b true_label [false_label]
if_fle a b true_label [false_label]
if_fgt a b true_label [false_label]
if_fge a b true_label [false_label]
if_fnear a b epsilon true_label [false_label]
if_seq a b true_label [false_label]
if_sne a b true_label [false_label]
if_slt a b true_label [false_label]
if_sle a b true_label [false_label]
if_sgt a b true_label [false_label]
if_sge a b true_label [false_label]
if_true value true_label [false_label]
if_false value true_label [false_label]
cmp_eq dst a b
cmp_ne dst a b
cmp_lt dst a b
cmp_le dst a b
cmp_gt dst a b
cmp_ge dst a b
cmp_feq dst a b
cmp_fne dst a b
cmp_flt dst a b
cmp_fle dst a b
cmp_fgt dst a b
cmp_fge dst a b
cmp_fnear dst a b epsilon
cmp_seq dst a b
cmp_sne dst a b
cmp_slt dst a b
cmp_sle dst a b
cmp_sgt dst a b
cmp_sge dst a b
exit