The executable, library, C++ namespace, headers, and tests are qasm. Merlin 16+ and Merlin 32 stay dialect names, including --syntax merlin and merlin32. The README is the QuickAsm front page: build, usage, the five matching coverage objects, and where the dialect docs live.
6.2 KiB
Merlin 16+ dialect
QuickAsm implements Merlin 16+ (Glen Bredon, Roger Wagner Publishing, 1988–89). The language contract is:
doc/manuals/Merlin_816_Macro_Assembler_Manual.pdf— base syntaxdoc/manuals/Merlin_16_Supplementary_Manual.pdf— 16+ additions
Merlin 16+ is the native language and the form the encoder consumes.
ORCA/M, LISA, Merlin 32, and ca65 sources are lowered to that form.
Identification, pcode, per-dialect pseudo-ops, and limitations are in
doc/SYNTAX.md.
Line format
LABEL OPCODE OPERAND ; comment
- A label, if present, starts in column 0 (no leading whitespace).
- Fields are separated by spaces or tabs.
*or;as the first non-blank character makes a comment line.- Opcode mnemonics are case-insensitive. Labels, macros, and equates follow
CAS SE/CAS IN(default: insensitive, typical FARMS).
Native 16+ source files (disk / IIgs editor)
On disk, Merlin 16+ source is not plain ASCII:
- Letters, digits, and punctuation normally have bit 7 set.
- Column tabs (label / opcode / operand / comment) are a single
$A0(space with the high bit set).$09and$89(TAB) are accepted too. - Literal spaces in quoted operands and comments are
$20(high bit clear), so columns can be split without tracking quotes. - Lines end with
$8D(high-bit CR), sometimes$0D/$0A. - Extra
$A0after the third separator is an ordinary space.
Host-side .s files (spaces, TABs, LF/CRLF, high bit clear) are also
accepted.
Processors
XC |
CPU |
|---|---|
XC OFF |
6502 only |
XC once |
65C02 |
XC twice (16+ default at startup) |
65802 / 65816 |
Startup: 65816 opcodes on, MX %11 (emulation). Put explicit MX / XC at
the top of each source so it matches a real 16+ machine regardless of FARMS.
SEP / REP update assembler MX from a constant immediate. MX %00–%11
(or 0–3) sets it without emitting code. M=0 / X=0 is 16-bit; 1 is 8-bit.
Addressing
Merlin does not write ASL A. Accumulator/implied is a bare mnemonic.
Immediate data starts with #. Addresses do not.
| Syntax | Mode |
|---|---|
LDA #$00 |
immediate (8 or 16 from M) |
LDA $10 |
direct page if value < $100 and not forced |
LDA $C000 |
absolute |
LDAL $E12000 / LDA >$E12000 |
long |
LDA: $10 |
force absolute (any 4th opcode character except L or D) |
LDA <$1234 |
force direct page (low byte) |
LDA |$04 |
force absolute |
LDA ($10),Y |
DP indirect indexed |
LDA [$10],Y |
DP indirect long indexed |
LDA $10,S |
stack relative |
LDA ($10,S),Y |
stack relative indirect indexed |
BEQ LABEL |
relative |
MVN src,dst |
block move (object: dest bank, src bank) |
Immediate byte/word selects (8-bit vs 16-bit from MX):
| 8-bit | 16-bit | |
|---|---|---|
#expr / #<expr |
low byte | low word (bytes 0–1) |
#>expr |
high byte | high word (bytes 1–2) |
#^expr / #Aexpr |
bank byte | bank word (bytes 2–3) |
Address-mode force prefixes (16+):
| Prefix | Mode |
|---|---|
<expr |
force direct page (low byte) |
|expr |
force absolute (16-bit) |
>expr |
force absolute long (24-bit) |
^expr |
absolute using the high word |
LDAL / lda: |
long / forced absolute |
; inside an operand with no preceding space is data (macro parameters
arg1;arg2). A ; after whitespace starts a comment.
Alternate mnemonics: BLT=BCC, BGE=BCS, TAS=TCS, TSA=TSC,
TAD=TCD, TDA=TDC, SWA=XBA. Long ALU forms LDAL, STAL, …
Undefined labels are 16-bit (absolute) until equated. Zero-page labels must be defined before use.
Expressions
Numbers: decimal, $hex, %binary. ASCII 'A' / "A".
* is the program counter at term position, multiply at operator position.
Default evaluation is left-to-right, no precedence (1+2*3 = 9). Enclose
in { } for algebraic precedence. --algebraic does that for all expressions
(FARMS bit; can break LAB1-LAB2/2 sources).
Operators, lowest to highest inside braces:
< = > # comparison (true=1, false=0); # is not-equal
+ -
* /
& . ! AND, OR, XOR
- unary minus
( ) are addressing, not grouping. Use { }.
Labels
- Global: start with a letter or extra-alphabetic (not a digit). Max 26 characters on a real 16+.
]NAMEas a label is a backward-only local (nearest previous).:NAMEis a local whose scope is the enclosing global labels.]NAME = expris a variable (redefinable). No forward refs.- Locals cannot be
ENT/EXTand cannot be the first label in a file.
Directives
ORG, OBJ, EQU/=, PUT, USE, VAR, SAV, DSK, END, DUM,
DEND, TYP, REL, LNK, ENT, EXT, MX, XC, CAS, ERR, CHK,
DATE, FLO (10-byte SANE), STRL, LST, EXP, TR, PAU, PAG,
AST, SKP.
PUT file inserts and assembles another source (search: directory of the
current file, -I dirs, then .; .s is optional). USE file loads
macros from that file without assembling its code. VAR e1;e2;… assigns
]1…]9 for a following PUT.
Data: HEX, DFB/DB, DA/DW, DDB, ADR, ADRL, DS,
ASC, DCI, INV, FLS, REV, STR, STRL.
ASC accepts hex bytes mixed into the string, empty strings, and
ASC #expr / #' / #" / #>.
Conditionals: DO / ELSE / FIN, IF / ELSE / FIN.
IF MX/2 tests M; IF "=]1" / IF #,]1 tests the first character of a
variable (macros). Nest ≤ 8 on real 16+.
Macros: MAC … EOM / <<<. Call by name, or PMC / >>>. Parameters
]1–]9 separated by ;. Nest ≤ 15 on real 16+. Labels in a macro body
(except EQU/=) are uniquified per expansion.
Loops: LUP n … --^. @ in a label becomes Z,Y,X,… per iteration (max 26).
Output
Absolute object (BIN) from ORG + DSK/SAV/-o.
REL / Linker.GS / OMF v2.1 is not implemented yet.
TYP accepts hex ($B3) or ASCII names (SYS, S16, CDA, EXE, BIN).
16+ additions (vs Merlin 16) that are first-class here
XC OFFCAS SE/CAS IN{algebraic}expressions and< = > #comparisonsSTRL- mixed hex in
ASC/STR/… andASC # TYPASCII filetypesSAVwithRELLST FILE,nameDATEoperands 1–8FLO(SANE extended)