Introduction to Merlin Assembly Language Programming on the Apple II Merlin is a powerful macro assembler for the Apple II family, originally developed by Glen Bredon and published by Southwestern Data Systems (later Roger Wagner Publishing). It supports the 6502 processor (and later variants like 65C02 and 65816 in updated versions) and runs under DOS 3.3 or ProDOS. Merlin includes an integrated editor, assembler, and disassembler (Sourceror). It's "TED-based," following conventions from earlier assemblers like TED II+. This guide focuses on practical aspects for writing and assembling 6502 programs using Merlin syntax. Programs are typically assembled to binary files that can be loaded and run on the Apple II. Key features include macro support, conditional assembly, and directives for managing large programs. For beginners: Start with simple programs at origin $800 (free memory area). Use the editor to enter code, assemble with ASM command, and run with BRUN or CALL. Merlin Source Code Syntax Merlin source files use a columnar format: Label (optional): Starts in column 1, alphanumeric, up to 15 characters, case-sensitive in later versions. Opcode/Mnemonic: 6502 instruction or assembler directive (pseudo-opcode). Operand: Argument(s) for the opcode, e.g., #$VALUE for immediate, $ADDR for absolute. Comment: Starts with ; or * (full-line comment with *). Example line: START LDA #$00 ; Load accumulator with 0 Lines can be up to 255 characters. Blank lines or full comments (*) are ignored. Merlin is case-insensitive for opcodes but case-sensitive for labels/macros in Merlin 32 (cross-assembler variant). Use tabs/spaces for alignment; no strict column requirements, but consistent indentation aids readability. From manual excerpts, source files have .T prefix under DOS, and assembly produces object code in memory or to disk. 6502 Opcodes and Mnemonics The 6502 has 56 instructions (151 opcodes including modes). Below is a table of common opcodes with syntax examples. For full list, see detailed references. Syntax uses $ for hex, # for immediate, () for indirect. Mnemonic | Description | Example Syntax | Bytes | Cycles ADC | Add with Carry | ADC #$05 ADC $44 ADC $4400,X | 2-3 | 2-4+ AND | Logical AND | AND #$FF AND ($44),Y | 2-3 | 2-5+ ASL | Arithmetic Shift Left | ASL A ASL $44,X | 1-3 | 2-7 BCC | Branch if Carry Clear | BCC LABEL | 2 | 2+ BCS | Branch if Carry Set | BCS LABEL | 2 | 2+ BEQ | Branch if Equal | BEQ LABEL | 2 | 2+ BIT | Bit Test | BIT $44 | 2-3 | 3-4 BMI | Branch if Minus | BMI LABEL | 2 | 2+ BNE | Branch if Not Equal | BNE LABEL | 2 | 2+ BPL | Branch if Plus | BPL LABEL | 2 | 2+ BRK | Break | BRK | 1 | 7 BVC | Branch if Overflow Clear | BVC LABEL | 2 | 2+ BVS | Branch if Overflow Set | BVS LABEL | 2 | 2+ CLC | Clear Carry | CLC | 1 | 2 CLD | Clear Decimal | CLD | 1 | 2 CLI | Clear Interrupt Disable | CLI | 1 | 2 CLV | Clear Overflow | CLV | 1 | 2 CMP | Compare Accumulator | CMP #$44 CMP ($44,X) | 2-3 | 2-6 CPX | Compare X | CPX #$44 | 2-3 | 2-4 CPY | Compare Y | CPY #$44 | 2-3 | 2-4 DEC | Decrement Memory | DEC $44 | 2-3 | 5-7 DEX | Decrement X | DEX | 1 | 2 DEY | Decrement Y | DEY | 1 | 2 EOR | Exclusive OR | EOR #$44 | 2-3 | 2-5+ INC | Increment Memory | INC $44 | 2-3 | 5-7 INX | Increment X | INX | 1 | 2 INY | Increment Y | INY | 1 | 2 JMP | Jump | JMP $4400 JMP ($44) | 3 | 3-5 JSR | Jump to Subroutine | JSR $4400 | 3 | 6 LDA | Load Accumulator | LDA #$44 LDA ($44),Y | 2-3 | 2-5+ LDX | Load X | LDX #$44 LDX $4400,Y | 2-3 | 2-4+ LDY | Load Y | LDY #$44 LDY $4400,X | 2-3 | 2-4+ LSR | Logical Shift Right | LSR A LSR $4400 | 1-3 | 2-6 NOP | No Operation | NOP | 1 | 2 ORA | Logical OR | ORA #$44 | 2-3 | 2-5+ PHA | Push Accumulator | PHA | 1 | 3 PHP | Push Processor Status | PHP | 1 | 3 PLA | Pull Accumulator | PLA | 1 | 4 PLP | Pull Processor Status | PLP | 1 | 4 ROL | Rotate Left | ROL A ROL $44,X | 1-3 | 2-7 ROR | Rotate Right | ROR A ROR $44,X | 1-3 | 2-7 RTI | Return from Interrupt | RTI | 1 | 6 RTS | Return from Subroutine | RTS | 1 | 6 SBC | Subtract with Carry | SBC #$44 SBC $4400,Y | 2-3 | 2-4+ SEC | Set Carry | SEC | 1 | 2 SED | Set Decimal | SED | 1 | 2 SEI | Set Interrupt Disable | SEI | 1 | 2 STA | Store Accumulator | STA $44 STA ($44),Y | 2-3 | 3-6 STX | Store X | STX $44 STX $44,Y | 2-3 | 3-4 STY | Store Y | STY $44 STY $44,X | 2-3 | 3-4 TAX | Transfer A to X | TAX | 1 | 2 TAY | Transfer A to Y | TAY | 1 | 2 TSX | Transfer SP to X | TSX | 1 | 2 TXA | Transfer X to A | TXA | 1 | 2 TXS | Transfer X to SP | TXS | 1 | 2 TYA | Transfer Y to A | TYA | 1 | 2 Notes: + indicates extra cycle if page boundary crossed. For full hex codes and variants, consult a complete opcode table. 6502 Addressing Modes The 6502 supports 13 addressing modes. Syntax varies by mode; Merlin uses standard notation. Mode | Syntax | Description | Example | Bytes | Cycles | Used By Accumulator | OPC A (or omitted) | Operates on accumulator. | ASL A | 1 | 2 | ASL, LSR, ROL, ROR Absolute | OPC $HHHH | 16-bit address. | LDA $4400 | 3 | 4 | Most load/store, jumps Absolute,X | OPC $HHHH,X | Address + X (with carry). | LDA $4400,X | 3 | 4+ | LDA, STA, etc. Absolute,Y | OPC $HHHH,Y | Address + Y (with carry). | LDA $4400,Y | 3 | 4+ | LDA, STA, etc. Immediate | OPC #$BB | 8-bit constant. | LDA #$05 | 2 | 2 | Loads, compares Implied | OPC | No operand, affects registers/flags. | CLC | 1 | 2 | Flags, transfers Indirect | OPC ($HHHH) | Jump to address stored at $HHHH. | JMP ($4400) | 3 | 5 | JMP only Indirect,X | OPC ($BB,X) | Zero-page address + X, then indirect. | LDA ($44,X) | 2 | 6 | Loads, stores Indirect,Y | OPC ($BB),Y | Indirect from zero-page, then + Y. | LDA ($44),Y | 2 | 5+ | Loads, stores Relative | OPC $BB (offset) | Signed 8-bit offset from PC+2. | BNE $F8 (label resolved by assembler) | 2 | 2+ | Branches Zero Page | OPC $BB | 8-bit zero-page address. | LDA $44 | 2 | 3 | Most instructions Zero Page,X | OPC $BB,X | Zero-page + X (wraps in page 0). | LDA $44,X | 2 | 4 | Loads, stores Zero Page,Y | OPC $BB,Y | Zero-page + Y (wraps in page 0). | LDX $44,Y | 2 | 4 | LDX, STX = extra cycle on page cross. Relative offsets are -128 to +127; assembler calculates for labels. Assembler Directives (Pseudo-Opcodes) in Merlin Merlin directives control assembly, data, and flow. Common ones: Directive | Syntax | Description | Example ASC | ASC "STRING" | ASCII string (high bit set with ' or "). | ASC "HELLO" CHK | CHK | Enables checksum in object code. | CHK DA | DA #EXPR | Define address (2 bytes, low-high). | DA #$300 DB | DB #EXPR | Define byte. | DB #$FF DS | DS EXPR | Define storage (reserve bytes, filled with 0). | DS 10 DSK | DSK FILENAME | Assemble directly to disk file. | DSK PROGRAM END | END | End of source (optional). | END EQU | LABEL EQU EXPR | Equate label to value. | ZERO EQU $00 HEX | HEX BB,BB,... | Hex bytes. | HEX 4C,00,03 LST | LST ON/OFF | Listing on/off. | LST OFF OBJ | OBJ $ADDR | Set object code address (rare). | OBJ $8000 ORG | ORG $ADDR | Set program origin. | ORG $300 PUT | PUT FILENAME | Include another source file. | PUT SUBROUT SAV | SAV FILENAME | Save object code to disk. | SAV PROGRAM USR | USR EXPR | User-defined opcode (advanced). | USR $A5 Macros: Define with MAC, end with EOM. Example: MAC ADD2 ADC #2 EOM Invoke with ADD2. Conditionals: USE IF, ELSE, FIN. Descriptions of Memory Areas All Apple II Computers $0000 - $00FF (0 - 255): Zero Page $0100 - $01FF (256 - 511): 6502 Processor Stack $0200 - $02FF (512 - 767): GETLN Line Input Buffer $0300 - $03CF (768 - 975): Free Space for Machine Language, Shape Table, etc. $03D0 - $03FF (976 - 1023): DOS, ProDOS, and Interrupt Vectors $0400 - $07FF (1024 - 2047): Text Video Page and Peripheral Screenholes $0800 - $0BFF (2048 - 3071): Text Video Page 2 or Applesoft Program and Variables $0C00 - $1FFF (3072 - 8191): Free Space for Machine Language, Shapes, etc. (might be overwritten if you use a lot of variables or BASIC program is long) $2000 - $3FFF (8192 - 16383): High Resolution Graphics Page 1 $4000 - $5FFF (16384 - 24575): High Resolution Graphics Page 2 $6000 - $95FF (24576 - 38399): Applesoft String Data (may have a little space free) (some BASIC programs move the variables all the way up to $4000 or even $6000) DOS 3.2 / 3.3 and Custom DOSes $9600 - $9CFF (38400 - 40191): Disk I/O Buffers $9D00 - $BFFF (40192 - 49151): DOS Routines (memory below $9600 can also be used for disk buffers via MAXFILES command) ProDOS $9600 - $99FF (38400 - 39423): BASIC.SYSTEM I/O Buffers $9A00 - $BEFF (39424 - 48895): Currently running SYS file $BF00 - $BFFF (48896 - 49151): ProDOS Kernel Global Page (memory below $9600 can also be used for disk buffers by opening more files) All Apple II Computers $C000 - $C0FF (49152 - 49407): Soft Switches and Status Locations $C100 - $C7FF (49408 - 51199): Peripheral Card Memory $C800 - $CFFF (51200 - 53247): Extended Memory for Peripheral Card in Use Apple IIe $C100 - $C2FF (49408 - 49919): Extensions to System Monitor $C300 - $C3FF (49920 - 50175): 80-Column Display Routines $C400 - $C7FF (50176 - 51199): Self-Test Routines $C800 - $CFFF (51200 - 53247): More 80-Column Display Routines Apple IIc and sometimes IIgs $C100 - $C2FF (49408 - 49919): Serial Firmware $C300 - $C3FF (49920 - 50175): 80-Column Firmware $C400 - $C4FF (50176 - 50431): Mouse Firmware $C500 - $C6FF (50432 - 50943): Floppy Disk Drive Firmware $C700 - $C7FF (50944 - 51199): AppleTalk Firmware $C800 - $CFFF (51200 - 53247): Extended Memory for Periph Card Apple IIe, IIc, IIgs, and II+ with Applesoft ROM Language Card $D000 - $F7FF (53248 - 63487): Applesoft Interpreter $F800 - $FFFF (63488 - 65535): System Monitor Apple II and II+ with Integer ROM Language Card $D000 - $D7FF (53248 - 55040): Programmer's Aid #1 ROM $D800 - $DFFF (55041 - 57343): Empty (No RAM or ROM) $E000 - $F7FF (57344 - 63487): Integer BASIC / Mini-Assembler / Sweet16 $F800 - $FFFF (63488 - 65535): System Monitor Apple IIe, IIc, and IIgs $D000 - $DFFF (53248 - 57343): Bank-Switched RAM (2 Banks RAM, 1 Bank ROM) $E000 - $FFFF (57344 - 65535): Bank-Switched RAM (1 Bank RAM, 1 Bank ROM) Apple II Zero Page Map ($0000-$00FF) Zero page is for fast access (shorter opcodes, fewer cycles). Apple II systems (Monitor, Applesoft, DOS) reserve many locations. Avoid conflicting uses. Below is a detailed map with descriptions (ranges where applicable). Address | Description $00-$05 | Unused/Unknown $06-$09 | Free space $0A-$0C | JMP to USR() user function routine $0D-$17 | Unused/Unknown $18 | First data track $19 | First data sector $1A-$1B | Shape pointer for DRAW $1C | Last COLOR used $1D-$1E | Free space $1F | Unused/Unknown $20 | Left margin (0-39/79, default 0) $21 | Width (1-40/80, default 40) $22 | Top margin (0-23, default 0) $23 | Bottom margin (0-23, default 23) $24 | Horizontal cursor position (0-39/79) $25 | Vertical cursor position (0-23) $26-$27 | Address of byte containing X,Y $28-$29 | Base address of text cursor's position $2A | Unused/Unknown $2B | Boot slot * 16 $2C | Lo-Res HLIN/VLIN endpoint $2D-$2F | Unused/Unknown $30 | COLOR value * 17 $31 | Unused/Unknown $32 | Text mask ($FF=normal, $7F=inverse, $3F=flashing) $33 | Prompt character $34-$35 | Unused/Unknown $36-$37 | Address of output routine $38-$39 | Address of input routine $3A-$4F | Unused/Unknown $50-$51 | Result of FAC to 16-bit integer conversion $52-$66 | Unused/Unknown $67-$68 | Start of BASIC program (default $0801) $69-$6A | Start of BASIC variables $6B-$6C | Start of BASIC arrays $6D-$6E | End of BASIC variables $6F-$70 | End of string data $71-$72 | Address to move string to $73-$74 | Start of string data $75-$76 | Current line number executing $77-$78 | Line number of END/STOP/BREAK $79-$7A | Address of executing line number $7B-$7C | Current DATA address $7D-$7E | Next DATA address $7F-$80 | Address of input or DATA $81-$82 | Last used variable's name $83-$84 | Last used variable's address $85-$9A | Unused/Unknown $9B-$9C | Pointer for $D61A and $F7D9 $9D-$A3 | Floating Point Accumulator (FAC) $A4 | Unused/Unknown $A5-$AB | Floating Point Argument Register (ARG) $AC-$AE | Unused/Unknown $AF-$B0 | End of BASIC program $B1-$B6 | Subroutine to increase string data pointer $B7-$BE | Subroutine to return character from string pointer $BF-$CD | Unused/Unknown $CE-$CF | Free space $D0-$D3 | Unused/Unknown $D4 | Error code flag $D5-$D6 | Unused/Unknown $D7 | Free space $D8 | Error flag (bit 7 set if handler used) $D9 | Unused/Unknown $DA-$DB | Line number of error $DC-$DD | Unused/Unknown $DE | Error code $DF | Unused/Unknown $E0-$E1 | Horizontal coordinate of HPLOT $E2 | Vertical coordinate of HPLOT $E3 | Free space $E4 | HCOLOR value (scaled 0-255) $E5 | Unused/Unknown $E6 | High byte of HGR plot address $E7 | SCALE value (0=256 pixels) $E8-$E9 | Shape pointer for XDRAW $EA | Collision counter for shape drawing $EB | Free space $EC-$ED | ROT value (0-63) $EE-$EF | Unused/Unknown $F0 | SPEED value (0-255, default 255=fast) $F1 | FLASH counter (for flashing text) $F2 | Last IN# slot * 16 $F3-$F5 | Unused/Unknown $F6-$F8 | Random number seed $F9 | HGR shape number $FA | HGR collision counter $FB-$FF | Unused/Unknown Safe free locations for custom use: $06-$09, $1D-$1E, $CE-$CF, $D7, $E3, $EB. Avoid Applesoft/Monitor areas unless overriding. Practical Tips and Examples Workflow: Boot Merlin disk, enter editor (E command), type code, assemble (ASM), save object (SAV or DSK), run with BRUN or CALL address. Memory Management: ORG $300 for small programs (above DOS). Use zero page for speed-critical variables. Debugging: Use LST ON for listings, Sourceror for disassembly. Monitor breakpoints via BRK. Apple II Specifics: Text output at $FDF0 (COUT), keyboard at $C000/$C010. Hi-res graphics: Softswitches at $C050+. Example Program: Simple beep loop. ORG $300 START LDA #$FF ; Load value STA $C030 ; Speaker toggle JSR DELAY ; Call delay JMP START ; Loop DELAY LDX #$FF LOOP DEX BNE LOOP RTS Assemble and run: CALL 768. Large Programs: Use PUT for includes, SAV for segmented saves. Cross-Assembly: Modern tools like Merlin 32 (compatible syntax) for Windows/Mac/Linux; convert old sources by adjusting directives (e.g., ASC to .AS). This guide condenses essentials; for advanced macros or 65816, consult full manuals. Validate Logic with Manual Tracing: For game mechanics like hit detection, manually simulate values (e.g., columns, rows) before coding. Use simple conditions first and add complexity (e.g., row checks) only if needed, as over-precision can make gameplay frustrating. Handle Multi-Digit Displays Carefully: When printing numbers, always support the full range (e.g., 0-255 for bytes). Use loops for digit extraction (subtract base like 10/100) and add $B0 for Apple II ASCII digits. Test edge cases like 0, 9-10 transition, and max value to avoid garbage characters. Keep Branches Short and Monitor Code Size: Assembly branches are limited to ±127 bytes. If adding code causes "bad jump" errors, invert logic (e.g., BMI instead of BPL) or restructure with JMP to labels. Use subroutines to modularize and reduce branch distances. Separate Instructions from Comments: Merlin parses lines strictly; avoid attaching comments directly to opcodes (e.g., 'ASL A ; 8' is safer than 'ASL A8'). Use full lines for comments to prevent syntax errors. Use Equates Consistently for Variables: Define all memory locations with EQU and use full names (e.g., OLD_EGG_Y) to avoid truncation errors like 'STA OLD_EGG'. This also improves readability in long code. Test Incremental Changes: After fixes, test specific scenarios (e.g., score=9 to 10, hits at different positions). Emulators like AppleWin can help debug assembly step-by-step. Graceful Exits and Mode Switches: For games, handle end states (e.g., 0 lives) by switching to text mode and returning to BASIC. This prevents hangs and allows easy restarts. Optimize for Gameplay Feel: If detection feels "sporadic," simplify algorithms (e.g., ignore rows for hits) or add features like bird nudge/egg drop on hit to enhance responsiveness without complex physics. Rewrite Buggy Routines from Scratch: If incremental fixes to subroutines like PRTNUM lead to persistent issues (e.g., off-by-one digits), start over with a simple loop-based approach to ensure clean logic. Document Code with Comments: Add comments for equates, subroutines, and logic (e.g., "Check if min + Y_OFFSET > 0 for top bounce"). This helped identify issues like carry flag misuse in the conversation.