All docs

VPy Language

Syntax, types, control flow, functions, arrays and built-in calls — the complete VPy reference.

VPy is a Python-inspired language that compiles to MC6809 assembly for the Vectrex console. All values are 16-bit integers. No floats, no booleans, no runtime exceptions.

Program Structure

Every VPy program has two special functions:

def main():
    # Called once at startup — initialization only
    SET_INTENSITY(127)
 
def loop():
    # Called every frame — game logic goes here
    draw_player()
  • main() runs once when the cartridge starts.
  • loop() runs every frame (~50 fps). All drawing and input goes here.
  • WAIT_RECAL() is automatically injected at the start of loop() by the compiler — do not write it yourself.

Minimal example

META TITLE = "HELLO"
 
def main():
    SET_INTENSITY(100)
 
def loop():
    PRINT_TEXT(-50, 0, "HELLO WORLD")

Variables and Constants

Global variables

Declared at the top level, persisted across frames:

player_x = 0
player_y = 0
score = 0

Local variables

Declared inside a function, scoped to that call:

def update_player():
    dx = joy_x * 2        # local
    player_x = player_x + dx  # player_x is global

Constants

const declares a compile-time constant stored in ROM, not RAM:

const MAX_ENEMIES = 8
const GROUND_Y = -70
const LEVEL_NAMES = ["LEVEL 1", "LEVEL 2", "LEVEL 3"]

Types and Values

VPy has a single type: 16-bit integer.

  • All arithmetic is 16-bit (values wrap modulo 65536).
  • 0 is false, any non-zero value is true.
  • No floating point. No booleans — use 1 and 0.
x = 42        # decimal
x = 0xFF      # hexadecimal
x = 0b1010    # binary
x = -7        # negative (compiled as 0 - 7)

Operators

CategoryOperators
Arithmetic+ - * / %
Bitwise& | ^ ~ << >>
Comparison== != < <= > >=
Logicaland or not
Compound+= -= *=

Chained comparisons work: 0 < x < 100 expands to (0 < x) and (x < 100).


Control Flow

# if / elif / else
if score > 100:
    level = 2
elif score > 50:
    level = 1
else:
    level = 0
 
# while loop
while lives > 0:
    lives -= 1
 
# for loop
for i in range(8):
    draw_enemy(i)
 
# switch / case
switch state:
    case 0:
        draw_title()
    case 1:
        draw_game()
 
# break, continue, return work as expected

Functions

def fire_bullet(x, y, direction):
    bullet_x = x
    bullet_y = y
    bullet_dir = direction
  • Up to 4 positional parameters.
  • Variables declared in main() are not accessible in loop() and vice versa — each function has separate scope.

Arrays

Arrays are declared with const (stored in ROM) or as globals (stored in RAM):

const coords = [10, 20, 30, 40]
val = coords[1]             # read by index
 
scores = [0, 0, 0]          # mutable array in RAM
scores[0] = 999

META Directives

META TITLE = "PANG"          # ROM header title (max 16 chars)
META MUSIC = music1          # background music asset
META ROM_TOTAL_SIZE = 65536  # enable multibank (optional)
META ROM_BANK_SIZE  = 16384  # bank size (optional, default 16KB)

Key Built-in Functions

FunctionDescription
SET_INTENSITY(n)Set beam brightness (0–127)
DRAW_LINE(x0, y0, x1, y1)Draw a line
DRAW_VECTOR("name", x, y)Draw a .vec asset at position
PRINT_TEXT(x, y, "text")Print text on screen
J1_X() / J1_Y()Joystick 1 axes (-128 to 127)
J1_BUTTON_1()Joystick 1 button (0 or 1)
PLAY_MUSIC("name")Start playing a .vmus asset
PLAY_SFX("name")Play a .vsfx sound effect
ANALOG_X() / ANALOG_Y()Raw analog joystick input
DEBUG_PRINT(val)Print value to IDE debug panel