This release brings multibank ROM support over the finish line, adds a one-click EPROM programmer to the IDE, and fixes a long-standing issue where SET_INTENSITY had no effect on DRAW_VECTOR brightness. The Core compiler is now deprecated — Buildtools is the recommended backend for all projects.
Multibank ROMs — up to 4 MB
The standard Vectrex cartridge is 32 KB. With a bank-switching circuit (a latch plus a few logic gates on the cartridge PCB), the hardware can address ROMs of up to 4 MB. VPy has always been designed with 4 MB as the ceiling — bank switching is automatic, and your game code never mentions banks at all. You only configure the ROM size:
META ROM_TOTAL_SIZE = 65536 # pick your target size (see table below)
META ROM_BANK_SIZE = 16384 # always 16 KB per bankThe compiler divides the ROM into 16 KB banks. Banks 0 through N−2 are switched in at $0000–$3FFF on demand; the last bank (helpers) sits fixed at $4000–$7FFF and is always visible. Bank switching happens automatically — DRAW_VECTOR, SHOW_LEVEL, UPDATE_LEVEL, and PLAY_SFX all switch to the right bank and back without any VPy code involvement.
Supported ROM sizes and recommended chips
ROM_TOTAL_SIZE | Banks | Capacity | Recommended chip | Notes |
|---|---|---|---|---|
32768 | 2 | 32 KB | 27C256 | Single-bank; no switching needed |
65536 | 4 | 64 KB | 27C512 | ✅ Fully tested, recommended starting point |
131072 | 8 | 128 KB | 27C010 | |
262144 | 16 | 256 KB | 27C020 | |
524288 | 32 | 512 KB | 27C040 / 29F040 Flash | |
1048576 | 64 | 1 MB | 27C080 / 29F080 | |
2097152 | 128 | 2 MB | M27C160 | |
4194304 | 256 | 4 MB | 29F320 Flash | Maximum supported size |
Current status: The 4-bank (64 KB) configuration is fully tested. Larger configs compile and the bank-switching infrastructure is in place, but cross-bank symbol resolution for more than 4 banks is still being completed.
The LEVEL_LOADED bug
During testing we found a subtle but nasty bug: SHOW_LEVEL_RUNTIME used CMPX #0; BEQ DONE to detect "no level loaded yet". That works fine unless the level asset happens to be the first object assembled in its bank — which gives it address $0000. The guard fires, the level is never drawn, and there is no error message.
The fix is a dedicated 1-byte LEVEL_LOADED flag: LOAD_LEVEL sets it to 1; SHOW_LEVEL checks it with TST >LEVEL_LOADED; BEQ DONE before loading the pointer. The null-pointer guard is gone.
EPROM programmer in the IDE
The IDE now has a dedicated EPROM Programmer dialog. Click the button in the toolbar, insert your blank chip into the TL866II+, and write your ROM without leaving Vectrex Studio.
What the dialog provides
Chip selection — choose your EPROM model from the list (27C256, 27C512, 27C010, etc.). The selection is remembered between sessions.
Write options — three checkboxes let you tune the programming behaviour, all persisted across sessions:
| Option | What it does |
|---|---|
| Skip erase | Don't blank-erase before writing (useful for chips already erased) |
| Skip verify | Don't read back and verify after writing (faster, less safe) |
| Unprotect | Remove write-protection before programming |
minipro detection — on macOS, if minipro is not installed the dialog shows a Install via Homebrew button that runs brew install minipro for you. On other platforms the install path is shown.
Live log — the full minipro output streams into a scrollable log panel inside the dialog so you can see progress and any errors without switching to a terminal.
Workflow
- Compile your project (produces
build/mygame.bin). - Click EPROM Programmer in the toolbar.
- Select the chip type and adjust options if needed.
- Insert your blank EPROM into the TL866II+ and click Write.
The .bin file from the last successful build is used automatically.
SET_INTENSITY now controls DRAW_VECTOR brightness
DRAW_VECTOR renders .vec vector assets using a custom draw loop called DSWM (Draw Sync With Mirrors). On real Vectrex hardware, line brightness is set by the T1 timer and the VIA Port A DAC. For months, calling SET_INTENSITY(64) before DRAW_VECTOR("ship") had no visible effect — vectors always drew at full brightness.
The root cause was a chain of small issues:
SET_INTENSITYdidn't write toDRAW_VEC_INTENSITY— it updated the BIOS variable but theDSWMloop was reading a separate RAM cell that was never written.- VIA Port A + Z-axis strobe wasn't being set —
DSWMwas missing the write to Port A and the CB2 strobe that tells the hardware DAC to latch the value. - T1 setup was in the wrong place — the T1 timer load must immediately precede
CLR VIA_t1_cnt_hito take effect; a displaced load was silently ignored. - Per-path intensity from
.vecdata — each path in a.vecfile carries an intensity FCB byte.DSWMwas only reading it on the first path.
All four issues are now fixed. SET_INTENSITY correctly programs the DAC before each DRAW_VECTOR call, and the per-segment FCB values in .vec files are respected.
def loop():
# Dim background vectors
SET_INTENSITY(40)
DRAW_VECTOR("background")
# Bright foreground objects
SET_INTENSITY(127)
DRAW_VECTOR("player")
DRAW_VECTOR("enemies")Typed variables — u8, i8, u16, i16
VPy now has four integer types declared with Python type-hint syntax. Before this release, every variable was an implicit i16 (2 bytes). Now you can pick the right size for the job:
health: u8 = 100 # 8-bit unsigned — 0–255
direction: i8 = 1 # 8-bit signed — -128 to +127
score: u16 = 0 # 16-bit unsigned — 0–65535
pos_x: i16 = 0 # 16-bit signed — -32768 to +32767| Type | Size | Range |
|---|---|---|
u8 | 1 byte | 0–255 |
i8 | 1 byte | -128 to +127 |
u16 | 2 bytes | 0–65535 |
i16 | 2 bytes | -32768 to +32767 |
Arrays scale too — a u8 array of 10 elements uses 10 bytes instead of 20. All code without type annotations continues to compile unchanged (i16 default).
One known edge case: const arrays with a u8 annotation can cause issues in complex games. Keep const arrays untyped as a workaround.
Level scrolling — SET_CAMERA_X
.vplay levels can be wider than the Vectrex screen. The new SET_CAMERA_X(x) builtin moves the viewport horizontally through the world. Objects outside the visible range are culled automatically — nothing extra needed in game code.
camera_x: i16 = 0
def loop():
joy = J1_X()
if joy > 20:
camera_x = camera_x + 2
if joy < -20:
camera_x = camera_x - 2
camera_x = clamp(camera_x, 0, 800) # keep inside world bounds
SET_CAMERA_X(camera_x)
SHOW_LEVEL()Object positions in .vplay files are world coordinates. SHOW_LEVEL() subtracts CAMERA_X from each object's X before drawing. LOAD_LEVEL() always resets CAMERA_X to 0.
Only horizontal scrolling is supported in this release.
Core compiler deprecated
The Core compiler (vectrexc, core/) is now in maintenance mode. It will not receive new features. All active development targets the Buildtools pipeline (vpy_cli, buildtools/).
If you have an existing Core project, migration is straightforward:
- Create a
.vpyprojfile next to your.vpyfile (see the compiler docs for the format). - In the IDE Settings panel, switch the backend selector to Buildtools (New).
- Rebuild — the output should be identical for single-bank projects.
Buildtools handles both 32 KB (single-bank) and 64 KB+ (multibank) games. For new projects, always start with Buildtools.