mirror of
https://github.com/marketideas/qasm.git
synced 2026-10-25 13:02:45 +00:00
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.
181 lines
6.2 KiB
Markdown
181 lines
6.2 KiB
Markdown
# 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 syntax
|
||
- `doc/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). `$09` and `$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 `$A0` after 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+.
|
||
- `]NAME` as a **label** is a backward-only local (nearest previous).
|
||
- `:NAME` is a local whose scope is the enclosing global labels.
|
||
- `]NAME = expr` is a **variable** (redefinable). No forward refs.
|
||
- Locals cannot be `ENT`/`EXT` and 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 OFF`
|
||
- `CAS SE` / `CAS IN`
|
||
- `{algebraic}` expressions and `< = > #` comparisons
|
||
- `STRL`
|
||
- mixed hex in `ASC`/`STR`/… and `ASC #`
|
||
- `TYP` ASCII filetypes
|
||
- `SAV` with `REL`
|
||
- `LST FILE,name`
|
||
- `DATE` operands 1–8
|
||
- `FLO` (SANE extended)
|