64tass Language Support
VS Code language support for the 64tass cross assembler
for the MOS 6502 family. Handles .asm, .s, .inc and .src.
Features
- Syntax highlighting for opcodes (documented and undocumented), directives,
numbers, strings and labels.
- Semantic highlighting on top of it, for what the grammar alone cannot tell
apart, like a constant from a label.
- Go to definition (F12) — labels and symbols, scope-aware and across
.include
files. Also opens the file under the cursor in an .include, .binclude or
.binary path.
- Find all references (Shift+F12), highlight occurrences of the symbol
under the cursor, and rename (F2). Rename can also update occurrences in
comments — shown separately in the preview, unchecked by default.
- Outline and breadcrumbs (Ctrl+Shift+O), nested by scope, with
_local
symbols grouped under the code label they belong to.
- Workspace symbol search (Ctrl+T) by fuzzy name, including files you have
not opened.
- Completion for mostly everything.
- Signature help while typing a call, in any of the four forms 64tass takes —
#mac a, b, .mac a, b, a bare mac a, b and fn(a, b) — with the parameter
you are on in bold, from the space after the name onwards. The popup follows the
form you are writing, so a .function invoked as a statement is shown as one.
- Hover on symbols and mnemonics, on a block closer like
.pend to see which
scope it ends, and on a macro or function to see the parameters it takes.
- Document links on the quoted paths of
.include, .binclude and
.binary — ctrl-click to open. Only paths that actually resolve become links,
so a broken one is visible as plain text.
- Quick fixes for a misspelled symbol or macro name (suggesting the closest
visible label) and for a block that was never closed.
- Expand selection (Shift+Alt+→) stepping
out through word, operand, operand list, line, each enclosing block, document.
- Folding for
.proc/.pend, .macro/.endm, .if/.endif and friends.
- Diagnostics for various problems, and from the real assembler on save when
64tass.assemblerPath is set.
- Inactive code greyed out - the branches of an
.if chain the conditions rule out,
and symbols nothing refers to.
- Directive help on hover, quoting the 64tass manual. Pragmas are completed and
documented too.
- Formatting (Format Document) aligning label, mnemonic, operand and comment.
- Cycle counts in a column left of the code.
Settings
64tass.caseSensitive
Default false. Mirrors 64tass's -C flag: when true, MyLabel and mylabel are distinct symbols.
64tass.cpu
The default is 6502i that includes all the "illegal" opcodes.
Decides which opcodes and register addressing modes are
recognised, mirroring 64tass's CPU selection flags. Accepts the same names as the
.cpu directive: default, 6502, 6502i, 65c02, 65ce02, 65dtv02,
65el02, 65816, r65c02, w65c02, 4510, 45gs02.
A .cpu "..." directive in a file is honoured automatically, and overrides the
setting for that file and everything it .includes.
The target decides what counts as an error: a mnemonic another CPU has (bra on
a plain 6502), an addressing mode it does not (lda $10,s), or an address too
wide for the form (sty $c000,x, which has no absolute,x). These are judged
against the target in force — so a file for a wider CPU should say so, with the
setting, a .cpu directive or the pragma below, or it is read as 6502i.
64tass.includePaths
Extra directories to search for .include and .binclude files, mirroring
64tass's -I flag. The including file's own directory is always tried first;
these are searched after it, in order. Relative paths are taken against the
workspace root.
"64tass.includePaths": ["libs", "../shared/asm"]
64tass.unusedSymbols
Default true. Greys out labels, constants and macros nothing in the same
program refers to, the way 64tass's own -Wunused reports them. Worth knowing
before you turn it off: a file of shared definitions uses none of its own
symbols, so it fades wholesale.
Where Format Document puts each field: mnemonic at 8, operand at 12, trailing
comment at 40 — the columns the 64tass distribution's own sources use. A field
that overruns its column gets a single space instead.
Formatting only ever changes the whitespace between label, mnemonic, operand and
comment. Full-line comments and .comment blocks are never touched, and a line it
cannot split confidently is left exactly as it is.
64tass.cycleCounts
Default false. Shows each instruction's cycle count in a column of its own,
between the folding arrows and the code:
12 ⌄ 4* lda $1234,x
13 sta $d020
14 6 jsr sub
15 2** beq loop
4* means one more if an indexed access crosses a page, 2** one more if a
branch is taken and two if it also crosses. Only the NMOS targets have timing
data, and a line whose addressing mode cannot be pinned down (lda elsewhere,
which may be zeropage or absolute) gets no count rather than a guessed one.
64tass.assemblerPath
Path to the real 64tass binary. When set, saving a file assembles it and
whatever the assembler says is shown alongside the extension's own checks
(marked 64tass build). That catches everything the static checks cannot reach —
expression typing, page and bank arithmetic, branch distance — and errors in an
.include are shown in that file, not in the one that includes it.
The target CPU, -C and 64tass.includePaths are passed automatically from the
other settings. 64tass.assemblerArgs adds anything else your build needs.
"64tass.assemblerPath": "/usr/bin/64tass",
"64tass.assemblerArgs": ["-D", "DEBUG=1"]
Nothing is written: the run sends its output to the null device, so it cannot
disturb your own build.
Pragmas
They are ordinary comments to 64tass — they change only how
this extension reads your code.
Type ; 64t and completion offers the prefix, then the pragma names, then the
values each one takes. Hovering a pragma line says what it does.
Case sensitivity
; 64tass-langserv: case-sensitive
; 64tass-langserv: case-insensitive
Overrides 64tass.caseSensitive for the file it appears in and everything that
file .includes. Useful when one workspace holds several projects that are
built with different -C settings. A nested file may override it again for its
own subtree.
CPU target
; 64tass-langserv: cpu 65816
For when the target is set on the command line rather than in the source. A
.cpu "65816" directive needs no pragma - it is picked up on its own.
Build-time defines
; 64tass-langserv: define standalone = 0
; 64tass-langserv: define below_io = $01
Acts as -D label=value for 64tass, for symbols your build supplies on the command line.
Defining them stops false "undefined symbol" / "duplicate symbol" reports, and lets the extension decide which
.if branches are inactive.
Which file to assemble
; 64tass-langserv: root ../main.asm
An include usually cannot be assembled on its own. Without this pragma, saving a
file assembles every root that includes it — a header shared by two programs is
part of both, and both are checked — or the file itself when nothing does.
Editor defaults
The extension ships two language-scoped defaults for .asm files. Both are
ordinary settings, so anything in your own settings.json wins:
"[64tass]": {
"editor.colorDecorators": false,
"editor.acceptSuggestionOnEnter": "smart"
}
colorDecorators is off because VS Code's built-in colour decorator mistakes a
hex-looking immediate operand such as cpx [#250](https://github.com/dabadab/64tass-langserv/issues/250) for a CSS colour and paints a
colour-picker box on it.
acceptSuggestionOnEnter is smart so Enter only accepts a suggestion when that
would actually change the text. Type a symbol in full and Enter opens the next
line; type a prefix and Enter still completes it. Tab always accepts.
Installation
Install 64tass Language Support from the
Marketplace,
or download a .vsix from Releases
and use Install from VSIX… in the Extensions view.
To run from source: yarn install, then F5 to launch the Extension Development Host.
Development
yarn test runs the unit suite and the compiler reference tests (the latter need a real
64tass, TASS_PATH). yarn bench measures the built server over LSP - startup, workspace
scan, diagnostics after an edit, every request kind - and yarn bench:history does the
same for past releases, so a regression can be traced to a version. Numbers are only
comparable on one machine; see bench/README.md.
License
MIT