Merlin 32

(C) 2011-2021 by Antoine VIGNAU and Olivier ZARDINI

> What is Merlin 32 ?

Merlin 32 is a multi-pass Cross Assembler running under Windows, Linux and Mac OS X targeting 8 bit processors in the 6502 series (such as 6502 and 65c02) and the 16 bit 65c816 processor.

It is compatible with Glen Bredon's Merlin 16+ syntax, including support for Macros, Pre-processor,  Logical Expressions, Conditional Operations, Variables, Loops, Local Labels...

It can build fixed position object code or relocatable
executables (OMF v2.1) as we can find on 16 bits Apple IIgs operating systems like Prodos 16 or GS/OS (S16, Exe, CDA, NDA, FST, PIF, Library, Tool...).

Merlin 32 
is part of the Brutal Deluxe's Cross Development Tools Project, a full set of utilities available on Windows (and other) platforms to enable the creation of new Apple IIgs software : 65c816 Assembler, 65c816 Disassembler, 65c816 Simulator, Graphic File Converter, Resource Catcher...


> About Merlin 32

The idea behind the creation of Merlin 32 was not to re-build a Merlin 16+ clone on a modern computer like a PC running Windows. Merlin 16+ is a great software including a full screen Text Editor, an 6502 / 65c02 / 65c816 Assembler, a Linker (including OMF support for Apple IIgs executable), a set of Disk Utilities (copy files, delete files, rename files...), a Disassembler (Sourceror) and much more. But Merlin 16+ is running on a single-process machine (the Apple IIgs) and this is now outdated. You can only perform one task after the other and there is no way to read / edit several source files at the same time (you have to save / close the first file before opening the other one). The editor, tailor made for assembly language editing, is limited to 24 lines and 80 columns, in 2 colors. You have to quit the editing of a source code to run it. And if it crash, you have to restart the operating system and restart everything (starting Merlin 16+, loading the source files...). Because of the Apple IIgs limitations, Merlin 16+ is limited (a source file can't be larger than 64 KB). There is no way to extend it while it is running inside an Apple IIgs and there is no guarantee you are not going to crash the system while you are trying to execute your code (no memory protection due to 65c816 architecture).

It was time to provide a way to continue the Apple IIgs programming with modern tools, on a modern computer. Everyone has its own habits, so there was no need to clone the Full Text Editor. There are many very good IDE that can be used to write 65c816 source code (Eclipse, Visual Studio, ...). You can also use your favorite Text editor (Emacs, PSPad, UltraEdit...) where several files can be edited together (you can copy / paste from one to the other, split the screen to see several files on the screen at the same time) and the syntax highlighting helps you to read the code (one color per category of items). You can take advantage of the screen resolution (a 23" screen provides a text editor of 55 lines and 230 columns !) and you can keep the source file opened in the editor while you are assembling / linking the program in another window and there is no risk anymore to crash the system while trying to execute the program in the emulator (or in a real Apple IIgs). The speed, even if it is not the core argument for a cross assembler, lets you assemble large projects in few seconds, instead of minutes (if not hours) on a real Apple IIgs. All the data exchanges are simplified. You can copy / paste source code from a Web Page or a text file and use it directly on your text editor. No need any more to convert the file into a valid Merlin 16+ format (high bit set to 1) before moving it to a disk image and use it under Merlin 16+. Because the source files are now stored on your modern computer as standard text files, you can use Source Control utilities like SVN to share your sources, backup them and  check for modifications using revision tool.

With Merlin 32, we provide the assembler and the linker to turn the source code (6502 / 65c02  / 65c816)  as a binary object (fixed position or relocatable with OMF support). All the edit job has to be done outside (with the text editor). You can assemble and link from a command window or you can use you IDE to associate the assembling syntax to a button. You probably have to work with CADIUS, another cross-development utility, to perform some basic tasks like indenting the source code (in the assembler style) or transferring the output of the assembly process (object code) or the source code into an Apple II disk image (.2mg, .po...).

There are already many cross-assemblers running on Windows capable to assemble 65c816 source code (xa, wla dxxasmmads...). Most of them were used to assemble source code targeting the Super Nintendo system (using a 65c816 like the Apple IIgs) or used as extension of 6502 cross assemblers dedicated to Commodore 64 or Atari XL computers. They could be used to assemble 65c816 code for the Apple IIgs but at least two major features are always missing :
- The capability to assemble source code using Merlin 16+ syntax (directives, macro, expressions, variables...)
- The capability to build relocated object code using OMF format (Apple IIgs 16 bit executables)

Merlin 16+ was one of the two most popular assemblers at the time for the Apple IIgs (the other one was Orca M) and many source codes are written using Merlin 16+ syntax (like our tools & games). We do not have to be compatible with Merlin 16+ syntax just to be able to re-assemble old files. We could have done a source converter to solve that issue. We have to be compatible with Merlin 16+ syntax because we have to make sure that the source code used into Merlin 32 on our PC running Windows could be sent back to the Apple IIgs to be also assembled with Merlin 16+. Even if we do 90% of the job with a cross-assembler, there are always few things that requires an Apple IIgs and its development toolset to build some parts like the Resource ones (menu, icon, about...). We don't say that Merlin 32 is going to replace Merlin 16+ and all the terrific development tools that already exist on the Apple IIgs platform. We say that we can speed up the process of writing code by using 90% of the time the cross assembler and 10% of the time the native Apple IIgs tools like Merlin 16+, Genesys, Iconed, GS Bug... With OMFAnalyzer tool, you can compare the output of Merlin 16+ and the output of Merlin 32 to ensure they both have generated the same object code (fixed address or relocatable) from the same source code.

The capability to build valid OMF relocatable executable files is something that the Super Nintendo cross-assemblers can't provide. The Apple IIgs is the most advanced software environment using the 65c816 processor and because of its operating system, it required a shared memory system capable to run several programs together in the same memory space. This implies memory management tools, dynamic loading of files, relocatable code, etc. At the opposite, the Super Nintendo games code run in ROM and don't have to deal with dynamic allocation or relocatable code.

Due to memory constraints, Merlin 16+ has some internal limitations :

- a Source File can't be larger to 64 KB
- a Source Line can't be larger than 255 characters
- a Label can't be larger than 26 characters
- the Operand part can't be larger than 80 characters
- the number of Externals is limited to 255
- Macros can be nested to a depth > 15
- Conditions
can't be nested to a depth > 8
- Symbol table is limited to 4096 symbols of length less than 12 and 2048 symbols of length 12 or over
 
Merlin 32
doesn't have any of these arbitrary limits. You can write your source code as you want but if you wish one day to send back the source code to the Apple IIgs and re-assemble it with Merlin 16+, check first your source code with the list above. 


> Merlin 32 output

The 65c816 addressing space is 16 MB, divided into 256 memory Banks of 64 KB each (from 00 to FF). Bank 00 contains the Stack and the Direct Page.


 The PC is 16 bit, so the code execution is limited within the current bank boundary ($FFFF + 1 = $0000). If a code is bigger than 64 KB, it has to be split into small chunks of code (each of them < 64 KB) and spread over the memory banks. The connection between the chunks of code from different memory banks use LONG addressing mode instructions (LDAL, STAL, JMPL, JSL...). In the Merlin 32 documentation, we will use the word Segment to define a chunk of 65c816 object code (with a size < 64 KB) located in one memory bank (not boundary cross). A Program, depending on its size, can use one or several Segments.

Merlin 32 lets you build 5 types of Programs :

> SINGLE SEGMENT / FIXED ADDRESS

The source files are assembled as One Binary File and it has to be loaded at a fixed address in memory (defined by the ORG directive of the source file) :



> MULTI SEGMENTS / FIXED ADDRESS

The source files (one set of files per segment) are assembled as Several Binary Files (one per segment) and they have to be loaded at a fixed address in memory (defined by the ORG directives of the source files) :



> MULTI SEGMENTS / FIXED ADDRESS /  MERGED

The source files (one set of files per segment) are assembled as One or Several Binary Files (several segments may be merged into one binary file). They have to be loaded at a fixed address in memory. If several segments are merged into one binary file, the beginning address of a segment is set as the end address + 1 of the previous segment. The Fixed Address of the First segment of the binary files are defined by the ORG directives of the Link file. The names of the binary files are defined by the DSK directives of the Link file  :



> SINGLE SEGMENT / RELOCATABLE

The source files are assembled as a Single OMF Segment file and will to be loaded by GS/OS at ANY address in memory (use of REL directive in the source file) :



> MULTI SEGMENTS / RELOCATABLE

The source files (one set of files per segment) are assembled as a Multi-OMF Segments file and will to be loaded by GS/OS at ANY address in memory (use of REL directive in the source files) :



Building a multi-segments programs (fixed address or relocatable) requires a definition file named Link File. The syntax of the Link file is described below, in the sections named Building Multi-Segments Fixed-Address Files and Building Multi-Segments OMF Files.

The Fixed Address binary files can be used in any system using a 65c816 processor like the Apple IIgs, the SNES, the Commodore PET 65816 CPU card, the CS/A 65816 CPU board, the CMD SuperCPU...

The Relocatable Programs can only be used on an Apple IIgs running GS/OS. The details about OMF Files data structure (Header + Object Code + Relocation Dictionary)  can be found in the Apple IIgs GS/OS Reference book, Appendix F : Object Module Format version 2.1. You can DUMP / COMPARE OMF Files using our  OMFAnalyzer Tool.


> Command List


If you do not provide any parameter on the command line, Merlin32 displays a quick reminder of the required parameters :

C:\AppleIIgs>Merlin32.exe
Merlin32.exe v 1.0 (c) Brutal Deluxe 2011-2021

   Usage :
Merlin32.exe  [-V]  <macro_folder_path>  <source_file_path>.

    Syntax
Merlin32.exe  [-V]  <macro_folder_path>  <source_file_path>

    Example
Merlin32.exe  -V  c:\AppleIIgs\Merlin\Library  c:\AppleIIgs\Source\Cogito\Cogito.s

Here are the parameters description :
        - The first parameter -V (Verbose) is optional. If set, it builds a text file containing the output of the assembly process
        - The second parameter
(<macro_folder_path>)is the path of the Folder containing all Macro definition files (*.Macs.s)
        - The third parameter
(<source_file_path>) is the path of the Master source file (or the Link file) to be assembled

Few remarks about the parameters required on the Command Line and the software behavior :
        - If the Windows File or folder paths contains Space characters, quote the path to avoid conflicts (Merlin32.exe -V "c:\Users and Settings\Merlin\Library"  c:\Source\Cogito\Cogito.s).
        - Any error occurring during the execution of the assembly process is immediately displayed on the screen.
        - If you are transferring Source files or Macro files from a disk image or FTP server, make sure you transfer the file as a text file.
 
      - If you are transferring Merlin source file (*.s) from a disk image, you may have to clear the high bit. You can use Cadius for that job (Cadius.exe CLEARHIGHBIT  <source_file_path>).
        - If you are opening Merlin source file (*.s) from a text file or by getting the code by a copy / paste from a web site, you may have to indent the source to make it easier to read with a Text editor on Windows. You can use Cadius for that job (Cadius.exe INDENT  <source_file_path>).

During the execution of the process, a progression status is displayed on the screen :

C:\AppleIIgs\Merlin\>Merlin32.exe -V C:\AppleIIgs\Merlin\Library C:\AppleIIgs\Source\Cogito\Cogito.s
Merlin32.exe v 1.0, (c) Brutal Deluxe 2011-2021
  + Assemble project files...
    o Loading Sources files...
        - Cogito.s
        - Cogito.Main.s
        - Cogito.Bout.s
    o Loading Macro files...
        - Int.Macs.s
        - Locator.Macs.s
        - Mem.Macs.s
        - Misc.Macs.s
        - Sound.Macs.s
        - Tool220.Macs.s
        - Util.Macs.s
    o Check for duplicated Macros...
    o Decoding lines types...
    o Process local/variable Labels...
    o Process Asterisk lines...
    o Build External table...
    o Build Equivalence table...
    o Build Variable table...
    o Process Equivalence values...
    o Replace Lup with code...
    o Replace Macros with Code...
    o Process MX directives...
    o Process Conditional directives...
    o Build Label table...
    o Check for duplicated Labels...
    o Check for unknown Source lines...
    o Check for Dum lines...
    o Compute Operand Code size...
    o Compute Operand Data size...
    o Compute Line address...
    o Build Code Line...
    o Check for Err lines...
    o Build Data Line...
    o Build Object Code...
  + Link project files...
    o Build OMF output file...
        => Creating OMF file '
C:\AppleIIgs\Source\Cogito\Cogito'
  + Create Output Text file...
     => Creating Output file '
C:\AppleIIgs\Source\Cogito\Cogito_Output.txt'

As a result, if everything went ok,  you get one binary file (fixed position object code or OMF file) and, if the -V option was enabled, a text file containing the output of the assembly process (Cogito_Output.txt) :

------+-------------------------+-------------+----+-------+------+--------------------+-------------------------------------------------------------------
 Line | # File             Line | Line Type   | MX | Reloc | Size | Address Object Code|  Source Code                                                     
------+-------------------------+-------------+----+-------+------+--------------------+-------------------------------------------------------------------
    1 |  1      Cogito.s      1 | Comment     | 11 |       |    0 | 0000               | *--------------------------*
    2 |  1      Cogito.s      2 | Comment     | 11 |       |    0 | 0000               | *                          *
    3 |  1      Cogito.s      3 | Comment     | 11 |       |    0 | 0000               | *          COGITO          *
    4 |  1      Cogito.s      4 | Comment     | 11 |       |    0 | 0000               | *                          *
    5 |  1      Cogito.s      5 | Comment     | 11 |       |    0 | 0000               | *      Brutal  Deluxe      *
    6 |  1      Cogito.s      6 | Comment     | 11 |       |    0 | 0000               | *                          *
    7 |  1      Cogito.s      7 | Comment     | 11 |       |    0 | 0000               | * Version: 2.0 du 26/08/94 *
    8 |  1      Cogito.s      8 | Comment     | 11 |       |    0 | 0000               | *--------------------------*
    9 |  1      Cogito.s      9 | Empty       | 11 |       |    0 | 0000               |                             
   10 |  1      Cogito.s     10 | Directive   | 00 |       |    0 | 0000               |                 mx        %00
   11 |  1      Cogito.s     11 | Empty       | 00 |       |    0 | 0000               |                              
   12 |  1      Cogito.s     12 | Directive   | 00 |       |    0 | 0000               |                 lst       off
   13 |  1      Cogito.s     13 | Directive   | 00 |       |    0 | 0000               |                 rel          
   14 |  1      Cogito.s     14 | Directive   | 00 |       |    0 | 0000               |                 dsk       Cogito.l
   15 |  1      Cogito.s     15 | Empty       | 00 |       |    0 | 0000               |                                   
   16 |  1      Cogito.s     16 | Directive   | 00 |       |    0 | 0000               |                 use       4/Int.Macs
   17 |  1      Cogito.s     17 | Directive   | 00 |       |    0 | 0000               |                 use       4/Locator.Macs
   18 |  1      Cogito.s     18 | Directive   | 00 |       |    0 | 0000               |                 use       4/Mem.Macs
   19 |  1      Cogito.s     19 | Directive   | 00 |       |    0 | 0000               |                 use       4/Misc.Macs
   20 |  1      Cogito.s     20 | Directive   | 00 |       |    0 | 0000               |                 use       4/Sound.Macs
   21 |  1      Cogito.s     21 | Directive   | 00 |       |    0 | 0000               |                 use       4/Tool220.Macs
   22 |  1      Cogito.s     22 | Directive   | 00 |       |    0 | 0000               |                 use       4/Util.Macs
   23 |  1      Cogito.s     23 | Empty       | 00 |       |    0 | 0000               |                 
   24 |  1      Cogito.s     24 | Comment     | 00 |       |    0 | 0000               | *--- Parametres Page Zero
   25 |  1      Cogito.s     25 | Empty       | 00 |       |    0 | 0000               |                           
   26 |  1      Cogito.s     26 | Equivalence | 00 |       |    0 | 0000               | Debut           =         $00
   27 |  1      Cogito.s     27 | Equivalence | 00 |       |    0 | 0000               | Arrivee         =         $04
   28 |  1      Cogito.s     28 | Empty       | 00 |       |    0 | 0000               |                           
   29 |  1      Cogito.s     29 | Equivalence | 00 |       |    0 | 0000               | proDOS          =         $e100a8
   30 |  1      Cogito.s     30 | Empty       | 00 |       |    0 | 0000               |                           
   31 |  1      Cogito.s     31 | Comment     | 00 |       |    0 | 0000               | *--------------------------
   32 |  1      Cogito.s     32 | Comment     | 00 |       |    0 | 0000               | * Initialisations d'entree
   33 |  1      Cogito.s     33 | Comment     | 00 |       |    0 | 0000               | *--------------------------
   34 |  1      Cogito.s     34 | Empty       | 00 |       |    0 | 0000               |                    
   35 |  1      Cogito.s     35 | Code        | 00 |       |    1 | 0000 : 4B          |                 phk
   36 |  1      Cogito.s     36 | Code        | 00 |       |    1 | 0001 : AB          |                 plb
   37 |  1      Cogito.s     37 | Empty       | 00 |       |    0 | 0002               |                    
   38 |  1      Cogito.s     38 | Macro       | 00 |       |    0 | 0002               |                 _TLStartUp
   40 |  1      Cogito.s     38 | Code        | 00 |       |    3 | 0002 : A2 01 02    |                 LDX       #$201                ; load tool call #
   41 |  1      Cogito.s     38 | Code        | 00 |       |    4 | 0005 : 22 00 00 E1 |                 JSL       $E10000              ; go to dispatcher
   42 |  1      Cogito.s     39 | Code        | 00 |       |    1 | 0009 : 48          |                 pha                           
   43 |  1      Cogito.s     40 | Macro       | 00 |       |    0 | 000A               |                 _MMStartUp                      
   45 |  1      Cogito.s     40 | Code        | 00 |       |    3 | 000A : A2 02 02    |                 LDX       #$202                ; load tool call #
   46 |  1      Cogito.s     40 | Code        | 00 |       |    4 | 000D : 22 00 00 E1 |                 JSL       $E10000              ; go to dispatcher
   47 |  1      Cogito.s     41 | Code        | 00 |       |    1 | 0011 : 68          |                 pla                           
   48 |  1      Cogito.s     42 | Code        | 00 | 2     |    3 | 0012 : 8D D8 AD    |                 sta       myID                
   49 |  1      Cogito.s     43 | Macro       | 00 |       |    0 | 0015               |                 _MTStartUp                      
   51 |  1      Cogito.s     43 | Code        | 00 |       |    3 | 0015 : A2 03 02    |                 LDX       #$203                ; load tool call #
   52 |  1      Cogito.s     43 | Code        | 00 |       |    4 | 0018 : 22 00 00 E1 |                 JSL       $E10000              ; go to dispatcher
   53 |  1      Cogito.s     44 | Macro       | 00 |       |    0 | 001C               |                 _IMStartUp                      
   55 |  1      Cogito.s     44 | Code        | 00 |       |    3 | 001C : A2 0B 02    |                 LDX       #$20B                ; load tool call #
   56 |  1      Cogito.s     44 | Code        | 00 |       |    4 | 001F : 22 00 00 E1 |                 JSL       $E10000              ; go to dispatcher
   57 |  1      Cogito.s     45 | Empty       | 00 |       |    0 | 0023               |                               
   58 |  1      Cogito.s     46 | Code        | 10 |       |    2 | 0023 : E2 20       |                 sep       #$20
   59 |  1      Cogito.s     47 | Code        | 10 |       |    4 | 0025 : AF 22 C0 E0 |                 ldal      $e0c022
...

The output file lets you check the pre-processor job (replace Macros with code, expand Lups, resolve local labels, compute expressions...), the assembler job (addressing mode, A X Y registers size, object code, ...) and the linker job (multi-org directives, addresses to be patched for relocated code, ...).

Here is a quick explanation for the columns available in the output file :
- Line : Global line number (1 to N).
- # File    Line : Source file number (
>1 if several source files are involved using PUT directive) and Local source file line number.
- Line Type : Type of source code line : Empty, Comment, Directive, Equivalence, Macro, Code or Data
- MX : Size for M (Accumulator) and X (X and Y Registers).This is helpful to understand if Merlin 32 is assembling 8 bit or 16 bit code. MX values are usually modified by MX directive or SEP / REP opcode.
- Reloc : For relocatable code, you will find here the number of bytes to be relocated and the shift operation performed on the address (>> 8, >>16...). If the label is EXTernal to the segment, the letter E is added in the column.
- Size : Number of bytes used to encode this line.
- Address   Object Code : Address (16 bit) of the line. If the ORG directive is used, the first address starts there. If the code is relocatable (REL directive), the first address is $0000. The bytes used to encode this line follow the address. We don't put more than 4 bytes / line.
- Source Code : The source code of the line has been processed (since we got it from source file) : Macros have been expanded, Loops has been exploded, local Labels have been replaced by unique names, Expressions have been resolved...

If you want to be sure that the source assembled with Merlin 32 on Windows create the same binary file than Merlin 16+ on GS/OS, you can compare the two result files with OMF Analyzer. If you are assembling a fixed position object code, use the COMPAREBIN command, if you are assembling an OMF file, use the COMPARE command.


> Merlin 32 Syntax


Because Merlin 32 uses the same syntax than Merlin 16+, the easiest way to learn about Merlin 32 syntax is probably to read documentation about Merlin 16+. You can pick up the Merlin 16+ documentation or any assembly book using Merlin 16+ syntax like Apple IIgs Machine Language for Beginners written by Roger Wagner.

The section provides information on writing assembly language programs with Merlin 32. You can skip this reminder if you are already familiar with Merlin 16+.

INDENTATION
An assembly source code is organized in 4 columns :

- LABEL :           Contains the identifier name for this line. It can be the label where to branch, the name of a new Macro, the name of a Variable...
- OPCODE :       Contains the action to be performed by the line. It can be a valid 65816 opcode, a Merlin 32 Directive, the name of a Macro to call...
- OPERAND :     Contains the parameter of the OPCODE. It can be the operand of the opcode, the Macro parameters, the value of the variable...
- COMMENT :    Starts with a
; character and contains a text explaining the Line purpose.

Merlin 32 is case sensitive for Labels, Macros, Operand values, Variables, Equates... You can write either LDA or Lda for opcode but PushLong and pushlong are not the same Macro !

We can use blank characters (SPACEs or TABs) to define the beginning / end of a column.

LABELOPCODEOPERAND COMMENT
      
      
proDOS
      
      
      
      
      
      
      
      
memERR
      
memERR1
      
      
      
      
      
      
memERR2
      
proKill
mx     
use    
       
=      
       
phk    
plb    
       
clc    
xce    
rep    
       
bcs    
rts    
PushWord
PushLong
PushLong
PushLong
PushLong
_TLTextMount
pla    
jmp    
       
dw     
adrl   
%00      
4/Int.Macs
         
$e100a8  
         
         
         
         
         
         
#$30     
         
memERR1  
         
#0       
#memSTR1 
#memSTR2 
#proSTR3 
#proSTR4 

         
initOFF  
         
1        
pTEMP    
             
             
             
             
             
             
             
             
             
             
             
             
; Memory Error
             
             
             
             
             
             
             
             
             
             
             
; Pathname  

Do not bother with indentation when you write your code in a Windows Text editor. Just add few Spaces or Tabs to separate columns. Once the lines have been written (or copy / pasted from another location), use CADIUS to indent automatically your source code :
CADIUS.exe  INDENT  <source_file_path>

After processing, the code is easier to read :

 SOURIS LDA BOUT ; ANCIEN BOUT=NOUVEAU BOUT
  STA BOUT1

  SOURIS0 JSR SLECT ; LECTURE SOURIS
  CPY #$FFFF
  BEQ SECR ; DONNEES NON DISPONIBLES

  SOURIS1 LDA A1 ; A1 POSITION ACTUELLE
  STA AP ; AP ANCIENNE POSITION
  LDA POSX
  LSR
  STA SOURIS2+1
  LDA POSY
  ASL
  TAX
  LDA TABLE,X
  CLC
  SOURIS2 ADC #$0000 ; CALCUL DE A1 (160*POSY+POSX)
 SOURIS      LDA   BOUT          ; ANCIEN BOUT=NOUVEAU BOUT
             STA   BOUT1

  SOURIS0     JSR   SLECT         ; LECTURE SOURIS
             CPY   #$FFFF
             BEQ   SECR          ; DONNEES NON DISPONIBLES

  SOURIS1     LDA   A1            ; A1 POSITION ACTUELLE
             STA   AP            ; AP ANCIENNE POSITION
             LDA   POSX
             LSR
             STA   SOURIS2+1
             LDA   POSY
             ASL
             TAX
             LDA   TABLE,X
             CLC
  SOURIS2     ADC   #$0000        ; CALCUL DE A1 (160*POSY+POSX)

Repeat the indent process as many times as you need.


COMMENT
A valid comment line starts with a * or a ; character. A comment line is never indented and does not have to enter into the LABEL / OPCODE / OPERAND / COMMENT scheme. If the line contains only blank characters like SPACEs or TABs, the line is considered as empty. If the first valid (non blank) character of the line is a ; with some blank characters before, the line is indented and the content is transferred in the COMMENT column.

*--------------------------------------------
*--  Check we have at least 512 KB available
*--------------------------------------------
; _FreeMem
                                               ; Memory Allocation
okIT2           PushLong  #0                   ; Ask for Shadowing
                PushLong  #$8000
                PushWord  myID

OPCODE
You can use all the 65c816 opcodes, with the following standard mnemonics :

ADC AND ASL
BCC BLT BCS BGE BEQ BIT BMI BNE BPL BRA BRK BRL BVC BVS
CLC CLD CLI CLV CMP COP CPX CPY
DEC DEX DEY
EOR
INC INX INY
JMP JML JSR JSL
LDA LDX LDY LSR
MVN MVP
NOP
ORA
PEA PEI PER PHA PHB PHD PHK PHP PHX PHY PLA PLB PLD PLP PLX PLY
REP ROL ROR RTI RTL RTS
SBC SEC SED SEI SEP STA STP STX STY STZ
TAX TAY TCD TCS TDC TRB TSB TSC TSX TXA TXS TXY TYA TYX
WAI WDM
XBA XCE

Opcodes modifying the Accumulator such as ASL, LSR, DEC and INC have no operand value. Write them ASL, not ASL A.

For Long addressing modes (24 bits address), you can add a L character at the end of the mnemonic :

ADCL SBCL
ANDL EORL ORAL
CMPL
LDAL STAL
JMPL

If you want to use alternate opcodes such as BGE (=BCS) or BLT (=BCC), you can easily define them as Macros.

ADDRESSING MODE
Merlin 32 handles all the 65c816 addressing modes, with the following syntax :

           ASL                 ; A          Implicit
           LDA   #$2000        ; #const     Immediate
           LDA   $C000         ; addr2      Absolute
           LDA   ($2000,X)     ; (addr2,X)  Absolute Indexed,X Indirect
           LDA   $2000,X       ; addr2,X    Absolute Indexed,X
           LDA   $2000,Y       ; addr2,Y    Absolute Indexed,Y
           LDA   ($2000)       ; (addr2)    Absolute Indirect
           LDA   [$2000]       ; [addr2]    Absolute Indirect Long
           LDAL  $E12000       ; addr3      Absolute Long
           LDAL  $E12000,X     ; addr3,X    Absolute Long Indexed,X
           LDA   $10           ; dp         Direct Page
           LDA   $10,X         ; dp,X       Direct Page Indexed,X
           LDA   $10,Y         ; dp,Y       Direct Page Indexed,Y
           LDA   ($10)         ; (dp)       Direct Page Indirect
           LDA   [$10]         ; [dp]       Direct Page Indirect Long
           LDA   ($10,X)       ; (dp,X)     Direct Page Indexed Indirect,X
           LDA   ($10),Y       ; (dp),Y     Direct Page Indirect Indexed,Y
           LDA   [$10],Y       ; [dp],Y     Direct Page Indirect Long Indexed,Y
           BEQ   LABEL         ; relative1  Program Counter Relative
           BRL   LABEL         ; relative2  Program Counter Relative Long
           LDA   ($10,S),Y     ; (sr,S),Y   Stack Relative Indirect Indexed,Y
           LDA   $10,S         ; sr,S       Stack Relative
           PEA   $1010         ; #const     Stack Immediate
           PEI   ($10)         ; (dp)       Stack Direct Page Indirect
           PER   $2000         ; 
#const     Stack Program Counter Relative Long

By convention, some Opcodes like PEA or PER receive addresses (starting with $as Operand even if it should be constants (PEA $A0A0 stores at the top of the stack the constant value #$A0A0, not the value found at address $A0A0).

The purpose of the Merlin 32 syntax is to remove any ambiguity regarding what the assembly process is supposed to build as output code.

For example, such code is not very clear :

           LDA   0             ; ???

Do we want to load the constant Zero in the accumulator (8 or 16 bit ?) or do we want to load the value located at address 0 (but is it Page Direct
$00, Current Bank address $0000 or Long address $00/0000 ?).

The first thing is to tell the difference between Data and Address. Data Operand starts with a
# while Address is everything else (numeric value, Label...) :

           LDA   #0            ; Data (Decimal)
           LDA   #$2000        ; Data (Hexadecimal)
           LDA   #%11110000    ; Data (Binary)

           LDA   0             ; Address (Decimal)
           LDA   $2000         ; Address (Hexadecimal)
           LDA   %00100000     ; Address (Binary)
           LDA   LABEL         ; Address (Label)
           LDA   LABEL+2       ; Address (Expression with Label)
 
The only times where Operands could be Data without using the
# as leading character is when we build expressions with an even number of Labels. For example, we compute here the number of bytes between two Labels :

           LDA   END-BEGIN     ; Data (Number of bytes between the two labels)

For immediate addressing modes (Operand is a Data), we have to figure out if the Operand is 8 bit or 16 bit. The following code :

           LDA   #1            ; Store 1 into the accumulator

could be assembled as :

+----+-------+------+--------------------+-----------------------------------------
| MX | Reloc | Size | Address Object Code|  Source Code                           
+----+-------+------+--------------------+-----------------------------------------
| 11 |       |    2 | 8000 : 
A9 01       |            LDA   #1        ; A is 8 bit (M=1)

or

+----+-------+------+--------------------+-----------------------------------------
| MX | Reloc | Size | Address Object Code|  Source Code                           
+----+-------+------+--------------------+-----------------------------------------
| 00 |       |    3 | 8000 : 
A9 01 00    |            LDA   #1        ; A is 16 bit (M=0)

Merlin 32 keeps the status of the M (Accumulator) and X  (X and Y registers) bits of the State Register for each line of the source code. In the Output text file, you can see them in the
MX column (0=16 bit, 1= 8 bit). The choice between 8 or 16 bit for Data Operand is based on the MX values. You can set the value of the MX bits using the MX directive in the source code. The MX directive use as Operand a value between 0 and 3, usually display using Binary format (%00, %01, %10 or %11) :

+----+-------+------+--------------------+-----------------------------------------
| MX | Reloc | Size | Address Object Code|  Source Code                           
+----+-------+------+--------------------+-----------------------------------------
| -- |       |      |                 
  |            MX    %00       ; Assemble next lines with M and X in 16 bit
| 00 |       |    3 | 8000 : A9 01 00    |            LDA   #1        ; A is 16 bit (M=0)
...
| -- |       |      |                    |            MX    %11       Assemble next lines with M and X in 8 bit
| 11 |       |    2 | 8003 : A9 01       |            LDA   #1        ; A is 8 bit (M=1)

Merlin 32 , furthermore, analyzes the Source Code for SEP or REP Opcodes and change the MX values based on the Operand value :

+----+-------+------+--------------------+-----------------------------------------
| MX | Reloc | Size | Address Object Code|  Source Code                           
+----+-------+------+--------------------+-----------------------------------------
| -- |       |    2 | 
8000 : C2 30       |            REP   #$30      ; Force M and X bits from Status Register in 16 bit
| 00 |       |    3 | 8002 : A9 01 00    |            LDA   #1        ; A is 16 bit (M=0)
...
| -- |       |    2 | 8005   E2 30       |            SEP   #$30      Force M and X bits from Status Register in 8 bit
| 11 |       |    2 | 8007 : A9 01       |            LDA   #1        ; A is 8 bit (M=1)

Unlike the
REP and SEP Opcodes, the MX directive doesn't change anything for code execution, it only impacts the assembly process.Up to you to control that 16 bit assembled code is called with 16 bit accumulator & registers.

Some Operand expressions may represent values larger than the Accumulator (or Register) size. By using some operators (
< > ^) right after the #Merlin 32 lets you select the bytes(s) you want to keep :

IMMEDIATE 8 BIT
   
We take only 1 byte from the Operand :

A9 00        LDA  #LABEL     ; with LABEL = $00E12000
A9 00        LDA  #<LABEL    ; with LABEL = $00E12000
A9 20        LDA  #>LABEL    ; with LABEL = $00E12000
A9 E1        LDA  #^LABEL    ; with LABEL = $00E12000

IMMEDIATE 16 BIT
   
We take 2 bytes from the Operand :

A9 00 20     LDA  #LABEL     ; with LABEL = $00E12000
A9 00 20     LDA  #<LABEL    ; with LABEL = $00E12000
A9 20 E1     LDA  #>LABEL    ; with LABEL = $00E12000
A9 E1 00     LDA  #^LABEL    ; with LABEL = $00E12000

     The PEA Opcode acts like an Immediate 16 bit Opcode, even if the Operand is seen as an address (no
#) :

F4 00 20     PEA  LABEL      ; with LABEL = $00E12000
F4 00 20     PEA  <LABEL     ; with LABEL = $00E12000
F4 20 E1     PEA  >LABEL     ; with LABEL = $00E12000
F4 E1 00     PEA  ^LABEL     ; with LABEL = $00E12000

 When the Operand is an Address, Merlin 32 has to figure out how many bytes (between 1 and 3) is used for the address encoding :

 +----+-------+------+--------------------+-----------------------------------------
| MX | Reloc | Size | Address Object Code|  Source Code                           
+----+-------+------+--------------------+-----------------------------------------
| 00 |       |    2 | 
8000 : A5 10       |            LDA   $10      ; Direct Page   (1 byte)
| 00 |       |    3 | 8002 : AD 00 C0    |            LDA   $C000    ; Absolute      (2 bytes)
| 00 |       |    4 | 8005 : AF 00 20 E1 |            LDA   $E12000  ; Long          (3 bytes)

Here is how Merlin 32 chooses among the 3 different addressing modes :

DIRECT PAGE
   
By default, Merlin 32 uses the Direct Page addressing mode for any Operand having a value in the range $00-$FF :

A5 10        LDA   $10      ; Direct Page  (1 byte)
A5 E1        LDA   LABEL    ; with LABEL = $E1

ABSOLUTE
    The Absolute address mode is the default on for any Address other than the range
$00-$FF. If the Operand is in the range $00-$FF, you can force an Absolute addressing mode by adding any character (except L) at the end of the Opcode :

AD 00 20     LDA  $E12000   ; Use only the 2 low bytes of the address
AD 00 20     LDA  $2000     ;
AD 11 00     LDA: $11       ; Force Absolute with :
AD 00 20     LDA  LABEL     ; with LABEL = $E12000
AD 00 20     LDA  LABEL     ; with LABEL = $2000
AD 11 00     LDA: LABEL     ; with LABEL = $11

LONG
    The Long addressing mode is forced by adding a
L character at the end of the Opcode or a > character at the beginning of the Operand :

AF 00 20 E1  LDAL  $E12000  ; 
AF 00 20 E1  LDAL  LABEL    ; with LABEL = $E12000
AF 00 20 AA  LDAL  LABEL    ; with LABEL = $2000  ($AA is the LABEL Bank)
AF 00 00 AA  LDAL  LABEL    ; with LABEL = $00    ($AA is the LABEL Bank)
AF 00 20 E1  LDA   >$E12000 ;
AF 00 20 E1  LDA   >LABEL   ; with LABEL = $E12000
AF 00 20 AA  LDA   >LABEL   ; with LABEL = $2000  ($AA is the LABEL Bank)
AF 00 00 AA  LDA   >LABEL   ; with LABEL = $00    ($AA is the LABEL Bank)


NUMBER
You can use decimal, hexadecimal or binary numerical data :

- Hexadecimal numbers start with a $ : $E12000, $00A0, $BD
- Binary numbers start with a
% and can use _ as visual separator : %01100101, %0000_1111_0000_1111
- Decimal numbers don't use any specific prefix :
15, 635, 32768

For opcodes accepting both data and addresses, you have to use the # as first character in the operand, in order to specify a data value :

A9 A0 00    LDA  #$00A0        ; Load a 16 bit constant numeric data 160 ($A0) in the accumulator.
AD 00 20    LDA   $2000        ; Load value stored at address $2000 in the accumulator.

For opcodes accepting only one type of operand (data or address) such as REP, PEA, JSR, MVN, STA... you don't need to add the # but is it always a good idea to insert it when data is involved (REP, SEP, PEA...).

STRING
The Apple IIgs recognizes only the following characters (the first one is the Space character) :

  ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  

A string is a set of ASCII characters enclosed by quotes (
') or double quotes (") :

48 65 6C 6C 6F    ASC  'Hello'        ; Using simple quote, the high bit is set to 0 (standard ASCII)
C8 E5 EC EC EF    ASC  "Hello"        ; Using double quotes, the high bit is set to 1 (for Text Screen encoding)

You can encode any ASCII character in a string by inserting before / in the middle / after the Hexadecimal value of the character(s) :

ErrorMsgLoad      ASC  'Can',27,'t load file !'    ; Can't load file, $27 is the hexadecimal value for '

DATA STORAGE
There are many pseudo opcodes used to define Data Storage (tables...).

HEX          define HEXadecimal data

00 01 02 03     HEX  00010203
00 01 02 03     HEX  00,01,02,03
00 01 02 03     HEX  0001,0203

   The operand consists of hexadecimal numbers (0-F) having even number of Hex digits (so 0F, not F). They may be separated by commas or may be adjacent. The $ is not required here.

DFB or DB          DeFine Byte

0A 0B 0E 0F     DFB  $0A,$0B,14,%0000_1111
EE              DFB  LAB+2                ; LAB Address is $FDEC, so LAB+2=$FD EE
FD              DFB  >LAB                 ; LAB Address is $ FD EC

  The operand consists of several bytes of data, separated by commas. It accepts all kinds of numeric formats (decimal, $hexadecimal and %binary) and arithmetic expressions. The low byte of the expression is always taken, except if you use the > sign (get high byte).

DDB          Define Double Byte

00 0A 00 0E     DDB  $000A,14
FD EC FD EE     DDB  LAB,LAB+2            ; LAB Address is $FDEC, so LAB+2=$FDEE

   The operand consists of several two-byte of data, separated by commas. It accepts all kind of numeric formats (decimal, $hexadecimal and %binary) and arithmetic expressions. The bytes are placed high-byte first.

DA or DW          Define Address or Define Word

0A 00 0E 00     DA   $000A,14
EC FD EE FD     DA   LAB,LAB+2            ; LAB Address is $FDEC, so LAB+2=$FDEE

  The operand consists of several two-byte of data, separated by commas. It accepts all kind of numeric formats (decimal, $hexadecimal and %binary) and arithmetic expressions. The bytes are placed low-byte first.

ADR           Define ADdRess - 3 bytes

0A 00 00     ADR   $0A
00 20 E1     ADR   SCREEN                 ; SCREEN Address is $E1/2000

   The operand consists of several three-byte of data, separated by commas. It accepts all kind of numeric formats (decimal, $hexadecimal and %binary) and arithmetic expressions. The bytes are placed low-byte first.

ADRL           Define Long ADdRess - 4 bytes

0A 00 00 00     ADRL   $0A
00 20 E1 00     ADRL   SCREEN             ; SCREEN Address is $E1/2000

   The operand consists of several four-byte of data, separated by commas. It accepts all kind of numeric formats (decimal, $hexadecimal and %binary) and arithmetic expressions. The bytes are placed low-byte first.

DS           Define Storage

00 00 00 00 00 00 00 00     DS   8        ; Reserve 8 byte of data, filled with 0x00
EE EE EE EE EE EE EE EE     DS   8,$EE    ; Reserve 8 byte of data, filled with 0xEE
A0 A0 A0 ...                DS   \,$A0    ; Fill memory with 0xA0 values until the next memory page

   Reserve space for Operand bytes of data (set to 0x00). You can choose to fill the reserved space with values other than 0x00 by providing a value (or an expression) as second operand. If you use the keyword \ as first operand, the memory is filled until the next page boundary. On relocatable code, the DS \ should only be used at the end of the file.

ASC           define ASCii text

48 65 6C 6C 6F    ASC  'Hello'        ; Using simple quote, the high bit  is set to 0
C8 E5 EC EC EF    ASC  "Hello"        ; Using double quotes, the high bit is set to 1

   This puts a delimited ASCII string in the object code. The simple quote is standard Ascii, used in Text files, GS/OS calls, file paths....

    The  double quotes (high bit set to 1) is used to display Text on Apple IIgs Text Mode screen (Page 1 or 2). The valid characters for Screen display are :

  ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  

   
The encoding goes from $A0 (Space) to $FF ( ).

DCI           Dextral Character Inverter

48 65 6C 6C EF    DCI  'Hello'        ; The high bit is set to 0, except for the last character
C8 E5 EC EC 6F    DCI  "Hello"        ; The high bit is set to 1, except for the last character

   This puts a delimited ASCII string in the object code, with the last character having the opposite high bit to the others.

INV           define INVerse text

08 05 0C 0C 0F    INV  'HELLO'        ; Inverse works only with Uppercase characters + Special characters
08 05 0C 0C 0F    INV  "HELLO"

   This puts a delimited ASCII string in the object code, in Inverse video format. The valid characters for Inverse Video are :

@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
  ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?

   The encoding goes from 
$00 (@) to $3F (?).

FLS           define FLaShing text

48 65 6C 6C 6F    FLS  'HELLO'        ; Flashing works only with Uppercase characters + Special characters
48 65 6C 6C 6F    FLS  "HELLO"

   This puts a delimited ASCII string in the object code, in Flashing video format. The valid characters for Flashing Video are :

@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
  ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?

   The encoding goes from $40 (@) to $7F (?).

REV           define REVerse text

6F 6C 6C 65 48    REV  'Hello'        ; The high bit is set to 0
EF EC EC E5 C8    REV  "Hello"        ; The high bit is set to 1

   This puts a delimited ASCII string in the object code, in backward order.

STR           define STRing with leading length byte

05 6F 6C 6C 65 48  STR  'Hello'       ; The high bit is set to 0
05 EF EC EC E5 C8  STR  "Hello"       ; The high bit is set to 1

   This puts a delimited ASCII string in the object code with leading length byte. Following hex values, if any, are not counted in the length.

STRL           define Long STRing with leading length word

05 00 6F 6C 6C 65 48  STRL  'Hello'   ; The high bit is set to 0
05 00 EF EC EC E5 C8  STRL  "Hello"   ; The high bit is set to 1

   This puts a delimited ASCII string in the object code with leading length word. Following hex values, if any, are not counted in the length. This is intended for use with GS/OS for Class 1 strings

LABEL
A Label is case sensitive and it has to be unique. Backward and forward references are allowed :

           JSR   GET_KEY
           ...
GET_KEY    LDA   $C000         ; Wait for a key
           BPL   GET_KEY
           BIT   $C010
           RTS

A label can't contain any characters less (in ASCII value) than the
Zero (Space, !, ", #, $, %, &, '