MADE FOR THE NES. READY FOR YOUR IDEAS. NO ACCOUNT NEEDED.
NESBASICBY NDB SOFTWARE

17 NES BASIC PROGRAMMING GUIDE

Compiling NES BASIC Programs into Standalone NES ROMs

You can turn a numbered NES BASIC program into a downloadable .nes file. That file contains your program, its kept sprite artwork and the NES-native runtime that executes it. Open it as an ordinary game in a compatible NES emulator. The browser, your .bas source and a separate NES BASIC cartridge are not needed to play the exported game.

This chapter covers the browser compiler. It does not change the BASIC language in the earlier chapters. You still work with small integer programs, NES graphics, controller input and the same memory limits.

What compilation means here

A .bas file is readable numbered BASIC source. A .nes file is a cartridge image containing a NES header and 6502-family machine code and data. Compilation validates your source with the native tokenizer and grammar checker, stores the accepted token records and artwork, and packages them with a native BASIC runtime. The resulting interpreter runs on the NES CPU. This is not a renamed text file, and it is not a browser app inside a ROM.

Compilation does not turn every BASIC statement into separately optimized machine code. Your game keeps the interpreter's execution speed. It does not gain extra RAM or new commands. The browser does not run the submitted program to decide whether compilation succeeds. Errors that depend on values or input can still occur while the exported game runs.

Your first standalone ROM: Hello NES

Open the playground. Click the NES screen and enter these four lines, pressing Enter after each one. On a phone, Keyboard opens a text-entry field; use Send to NES for each line. You can also open the Hello NES source file with Open .bas file.

NES BASIC
10 CLS
20 PRINT "HELLO NES!"
30 PRINT "MY FIRST NES ROM"
40 END

Type RUN and press Enter. The screen should show HELLO NES! and MY FIRST NES ROM. Keep a source backup with Download .bas. That backup lets you edit and recompile your program later; the exported game is a separate file.

The tested Hello NES listing in the local playground, with Compile to .NES beside Download .bas.
The tested Hello NES listing in the local playground, with Compile to .NES beside Download .bas.

Click Compile to .NES beside Download .bas. A dialog asks for a name. Enter HELLO_NES; the .nes extension is supplied. The suggested name comes from an imported file or example when available. Spaces and unsafe filename characters are normalized, and the dialog shows the actual download name.

The filename dialog asks what to call the standalone ROM before compilation begins.
The filename dialog asks what to call the standalone ROM before compilation begins.

Choose Compile & Download. Progress and any source-line error appear in the dialog. Cancel compilation or close the dialog to stop the worker; your editor remains intact. On success, the browser starts downloading HELLO_NES.nes. Your browser decides whether to use its Downloads folder or ask where to save it. The current ROM is 131,088 bytes, including its header.

Successful compilation starts the HELLO_NES.nes download and offers Run Compiled ROM.
Successful compilation starts the HELLO_NES.nes download and offers Run Compiled ROM.
HELLO_NES.nes in the browser download list after compilation.
HELLO_NES.nes in the browser download list after compilation.

Choose Run Compiled ROM in the success dialog. The playground boots the actual exported bytes in a separate NES machine. Hello NES runs immediately; you do not type RUN in the exported game. END leaves the final screen visible. Restart ROM or Reset reboots that game. Stop freezes compiled playback. Return to Editor brings back your original source, artwork and six cartridge saves. Close the filename dialog to continue typing normally. If you edit source or artwork, compile again to update the ROM.

The four-line listing above has been compiled, downloaded from the actual playground and booted independently. You can compare your result with the tested Hello NES ROM.

Open the download in an independent emulator

Use a NES emulator that supports the cartridge configuration in the memory table below. In Mesen, choose File → Open and select HELLO_NES.nes. Open the ROM itself, not a .bas file, recovery snapshot or emulator save state. Reset or power-cycle the emulated cartridge: the greeting should appear again without loading source or an external .sav file.

The downloaded Hello NES ROM running in the independent Mesen emulator core.
The downloaded Hello NES ROM running in the independent Mesen emulator core.

This screenshot is actual Mesen output from the downloaded tutorial ROM. The project has also tested exports through the FCEUmm core used by 8-Bit Impostor. Passing one emulator is useful evidence, but it does not establish physical flash-cartridge compatibility. See the Mesen loading instructions for its file-opening workflow; menus can vary by release.

In 8-Bit Impostor, save the downloaded .nes into Files or another location accessible to the app's ROM importer. Choose LOAD ROM, select the file, then open its library entry and choose PLAY CARTRIDGE. Import it as an ordinary NES game. Do not open the built-in NES BASIC programming environment and try to LOAD the file as BASIC source. The embedded runtime belongs to your exported game, so an older built-in programming cartridge does not supply that game's language implementation. Use the app's ordinary game controls, including its connected-controller settings. App menu labels depend on the installed version.

For a useful independence check, close the playground and start your game from its downloaded ROM. No web connection is required for ROM execution. Reopening the web editor itself still requires its site assets; this feature does not install an offline web application.

Artwork, controller input and sound

Kept artwork from EDITSPR is embedded at compilation: all 32 editable 8×8 patterns, IDs 8–39. Built-in sprite IDs 0–7 are already in the runtime. Finish with Keep or discard pending artwork changes before compiling. A plain .bas file contains source only, so import it into the session holding your desired art or recreate/LOAD the art before exporting. Native SAVE preserves source and art in the editor's cartridge slots.

During compiled playback, touch buttons, a connected gamepad and the saved keyboard controller mappings operate controller one. The default keyboard mapping is arrows for the D-pad, X for A, Z for B, Enter for Start and right Shift for Select. Your editor's saved typing/controller mode is not changed. An independent emulator uses its own input mappings. JOY(2) also exists in the runtime; the web player's controls expose player one.

Text INPUT uses the cartridge's controller-operated NES keyboard. Use the D-pad to select characters, A to type, B to delete and Start to submit. The compiled game does not depend on the browser's source-typing bridge. Enable Sound with a click in the playground. In an independent emulator, check its audio settings and the device's output volume. BEEP and SOUND remain native NES audio, with the same timing and channel limits.

Command compatibility reference

All 48 existing program-level tokens can be exported. Thirteen prompt/editor tokens cannot be placed in numbered game code. This table covers all 61 keyword tokens; full syntax and examples remain in the searchable command reference.

KeywordStandalone exportNative behavior and limits
ABSSupported in programsSigned 16-bit; ABS(-32768) overflows.
ASCSupported in programsFirst byte of nonempty string, not a browser key code.
BEEPSupported in programsPulse 1 note 69 for 12 native frames; test APU output.
CATEditor only; rejected in numbered codeFILES alias; editor directory, not game state.
CHR$Supported in programsOne byte 0–255; glyph visibility differs from byte validity.
CLEARSupported in programsResets values, arrays, control frames, sound, sprites and seed; retains source/art.
CLSSupported in programsNative terminal/canvas clear; preserve native mode behavior.
COLORSupported in programsShared foreground/background palette indices 0–63 excluding 13/29/45/61; sprite palette fixed.
DEMOSEditor only; rejected in numbered codeEditor library; not game execution.
DIMSupported in programsA–Z numeric arrays, one/two dimensions, inclusive bounds, 1,216 shared cells; needs 32 KiB banked RAM layout.
EDITSPREditor only; rejected in numbered codeEmbed 512 bytes of kept art at compile time instead; 32 patterns IDs 8–39.
ENDSupported in programsFinish safely, stop audio and retain final game scene.
ERASEEditor only; rejected in numbered codeEditor project deletion; no persistent game-state API.
FILESEditor only; rejected in numbered codeSix editor project slots; not compiled game saves.
FILLSupported in programsSCREEN 1, four-connected canvas fill, 64-seed complexity bound.
FORSupported in programsInteger loops, negative/zero-trip support, eight shared FOR/GOSUB frames.
GOSUBSupported in programsLiteral existing line, eight shared frames; preserve colon return position.
GOTOSupported in programsLiteral existing line 1–65535; compiler validates references.
HELPEditor only; rejected in numbered codeNative editor help; link compiler help in browser/guide instead.
HIDESupported in programsSprite slot 0–63; native OAM update.
IFSupported in programsNonzero numeric condition; THEN literal line or inline statement; no ELSE; false skips whole line remainder.
INKEY$Supported in programsNo parentheses; eight-event FIFO; standard controller edges 128–135 in RUN, optional keyboard peripheral.
INPUTSupported in programsOne scalar/string/array cell; retain controller-operated native text keyboard and retry behavior.
INTSupported in programsInteger identity; no floating point.
JOYSupported in programsPort 1/2 held-button mask; port 2 unavailable after optional keyboard handshake until reset. Avoid automatic handshake in compiled play.
KILLEditor only; rejected in numbered codeERASE alias; not game persistence.
LEFT$Supported in programsNonnegative byte count clamps; shared string storage limits.
LENSupported in programsByte length, including NUL, not Unicode characters.
LETSupported in programsOptional LET, A–Z/A$–Z$, typed values and indexed numeric assignment; no long identifiers.
LINESupported in programsSCREEN 1, inclusive endpoints and clipping; long segments may take many frames.
LISTEditor only; rejected in numbered codeEditor listing and paging, not program bytecode.
LOADEditor only; rejected in numbered codeRestores editor source/art; not runtime game progress.
LOADDEMOEditor only; rejected in numbered codeEditor library replacement.
LOCATESupported in programsColumns 0–27, rows 0–21; restores text from graphics.
MEMSupported in programsNative remaining program/line/array/string diagnostics, not general host memory.
MID$Supported in programsOne-based start, optional nonnegative count; byte strings.
MOVESupported in programsSCREEN 1, visible slot, x 0–255/y 1–239; native sprite bounds.
NEWEditor only; rejected in numbered codeEditor operation, not embedded program statement.
NEXTSupported in programsExplicit matching FOR variable; native overflow/stack errors retained.
PRINTSupported in programsNative text, typed expressions, semicolon/comma separators; text paging uses controller; exits graphics.
PSETSupported in programsSCREEN 1, 128×64 one-bit canvas; clipped points no-op.
RECTSupported in programsSCREEN 1, opposite corners, outline, not filled width/height rectangle.
REMSupported in programsRest of line literal; compiler must not treat keywords/colons in comments as instructions.
RETURNSupported in programsMatching GOSUB frame, native line/colon return semantics.
RIGHT$Supported in programsNonnegative byte count clamps; string memory checks.
RNDSupported in programsDeterministic 16-bit LFSR, returns 0..N-1 for N 1–32767; RUN resets seed; no RANDOMIZE.
RUNEditor only; rejected in numbered codeExport boot calls native execution entry automatically; named editor RUN not a game statement.
SAVEEditor only; rejected in numbered codeSource/art only; no scores/variables persistence promise. Header RAM requirement remains.
SCREENSupported in programs0=text, 1=graphics; native clear/font/palette/sprite transition behavior.
SGNSupported in programsSigned integer result -1/0/1.
SOUNDSupported in programsChannels 1–4; notes 33–96 or noise 0–15, durations 0–32767 native frames; NTSC; no DMC.
SPRCOLSupported in programsVisible unclipped 8×8 box overlap, not pixel collision; edge touching false.
SPRITESupported in programsSCREEN 1, 64 slots, 8 per scanline, art 0–39; embed editable art alongside source.
STEPSupported in programsNonzero integer step; default 1; not a standalone command.
STOPSupported in programsTermination like END; no CONT; preserve final frame.
STR$Supported in programsSigned decimal integer string, no positive leading space; string arena limits.
THENSupported in programsLiteral target or inline statement; validate references even in untaken branches.
TILESupported in programsSCREEN 1, 32×30 tile positions, pattern 0–255; native shared patterns, no per-call palette.
TOSupported in programsInclusive loop limit; not a standalone command.
VALSupported in programsStrict whole signed decimal string, optional surrounding spaces, -32768..32767; errors on garbage.
WAITSupported in programs0–32767 native frames; pause freezes emulation; statement work takes additional time.

Normal numeric/string assignments, arithmetic and documented comparisons also work. ELSE, WHILE, DATA/READ/RESTORE, long identifiers, floating point, string arrays and arbitrary machine-code access have not been added. TO, STEP and THEN are grammar components used in the documented statements, not independent executable commands.

Memory and cartridge limits

ResourceExport limit or requirement
Numbered programUp to 128 lines and 3,072 token-record bytes
Physical source lineUp to 95 printable ASCII characters
BASIC line numbers1–65535; branch targets must name existing literal lines
Numeric valuesSigned 16-bit integers, −32768 through 32767
Numeric arrays26 names; one or two dimensions; 1,216 shared cells
Strings26 variables; 63 bytes per stored value; 256-byte shared string pool
Control stackEight shared FOR/GOSUB frames
Graphics128×64 one-bit canvas; existing native color limits
Sprites64 slots; up to eight per NES scanline; existing native priorities
Kept custom artwork512 bytes: 32 patterns, IDs 8–39
ROM fileNES 2.0, mapper 1/submapper 0, SXROM-compatible MMC1; 131,088 bytes
Cartridge memory128 KiB PRG ROM, 8 KiB CHR RAM, 32 KiB banked PRG RAM/NVRAM
Video targetNTSC; PAL timing and hardware acceptance are not established

A large amount of physical RAM on a flash cartridge is not enough by itself. The emulator or cartridge firmware must expose the exact banked memory layout and interpret the NES 2.0 header correctly. A generic mapper-1 board with only 8 KiB of work RAM is insufficient. Keep the generated header intact.

Compilation errors and runtime errors

Finish submitting the current line before compiling. The compiler also blocks unsubmitted text in the Keyboard field, a previously rejected numbered edit, uncommitted sprite work, an active cartridge operation and unfinished INPUT. These checks prevent accidentally exporting an older listing or partial art. An empty program must be given numbered lines first.

If a program branches to a missing line, fix the indicated source line and compile again. Literal GOTO, GOSUB and THEN targets are checked even in a branch that was not taken during your editor test. Unsupported editor statements in numbered code are rejected. Cancelled, timed-out or failed compilations do not replace your source or produce a successful new ROM.

A successful compile checks format and static syntax; it does not prove every execution path safe. Division by zero, overflow, out-of-range array subscripts, string-pool exhaustion and a full control stack can still fail at runtime. The playground shows the native error and BASIC line when available. Return to Editor, reproduce the failure with a small input, correct it, then recompile. See errors and recovery for the native messages.

For a game, test reset, repeated play, controller input, text INPUT, sprites, collisions, sound and every branch you can reach. Try the actual download in another compatible emulator, using fresh game RAM instead of a saved state. A program that works only after loading a previous editor snapshot is not a successful standalone test.

Running Your NES BASIC Programs on a Real Nintendo

Physical NES and flash-cartridge testing is still pending. This is a preparation and test procedure, not certification of a cartridge model or firmware version. Use your own NES or compatible console and hardware that explicitly supports the mapper, RAM banks and NES 2.0 format listed above.

First compile Hello NES and confirm the downloaded file boots independently in an emulator. Keep the known-working original ROM and its source backup. Check the flash cartridge maker's current manual for its console connector, SD-card preparation, firmware, mapper library and safe shutdown procedure. Do not buy hardware solely on the assumption that “supports MMC1” guarantees this export's SXROM memory configuration.

With the console powered off, prepare the card using the manufacturer's instructions and copy HELLO_NES.nes to a normal ROM folder. Eject the card safely, insert the card and flash cartridge, then power on. Select the ROM in the cartridge menu and inspect its ROM information if that menu provides it. Confirm mapper 1 and the full 32 KiB RAM requirement before treating the hardware as a valid test target. Launch the file as a game.

For example, the EverDrive N8 Pro manual describes ROM Info fields for mapper, memory and mapper support, plus a Start Game action. Those menu facilities help inspect a candidate configuration; they do not establish that this exact NES BASIC ROM has passed on that device. Follow the manual for your particular hardware revision and firmware.

The greeting should appear without an editor, keyboard, source injection or preloaded save file. Reset the console and verify that it starts again. Then test a small controller program, graphics and audio. Record the console, video region, cartridge model, firmware and ROM hash with the results. Stop and investigate a black screen, wrong header interpretation or missing RAM; do not work around it by stripping the NES 2.0 header or claiming a pass from an emulator instead. Physical validation belongs to the hardware milestone.

Saving and sharing a compiled game

Persistent game-state or high-score saving is not implemented. The ROM's NVRAM metadata describes the cartridge hardware; it does not provide a BASIC game-save API. SAVE/LOAD remain editor source-and-art commands and are rejected inside numbered game code. Exporting does not turn the editor's six project slots into game slots. Reset starts the exported program afresh.

An emulator or flash cartridge may offer its own save states. Those are host features with their own compatibility rules; they are not portable source backups or an implemented in-game save system. Keep .bas source, artwork backups and corresponding runtime sources separately from test save states.

The embedded native runtime retains GPL-2.0-or-later licensing. JSNES's Apache-2.0 browser emulator is not embedded in the exported game. Before redistributing a ROM containing the runtime, review the applicable notices and corresponding-source requirements. No new commercial exception or redistribution license has been applied by this feature. Download the matching compiler/runtime source and notices and read source and licenses.

Compilation happens on your device, without an account or a source-upload service. It does not automatically publish your program. Session recovery belongs to the browser and origin you are using; Download .bas and native artwork saves remain useful backups while you develop.