- ASSEMBLER SECTION - - EXPRESSIONS - Expressions can be made from decimal numbers, hex numbers by using "$", binary numbers by using "%", single ASCII characters with a "'" (single quote), and label names. Any of these values can be mixed with math operators +-*/, ! (bitwise OR), & (bitwise AND), ^ (exclusive OR), \ (modulo), and unary -. The vertical bar | can be used in place of !. There are four logical operators that will return values of either 0 (false) or 1 (true). These are <, >, =, and # (not equal). These operators are primarily for conditional assembly .IF statement use. There are also special unary operators that refer to the low byte, high byte, and bank byte (24-bit highest byte) of the calculated expression. these operators are <, >, and ^. There is no operator precedence. All math is evaluated left to right, with the exception of leading unary operators <, >, ^, and - which are done after the rest of the expression has been evaluated. Do notputany spacesin the middle of expressions. Spaces are consideredby MAE to be separators between different expressions. Examples of valid expressions: LDA #'A-$20 ;= $21 LDA #-1 ;= $FF LDA #-1+2 ;= $FD (the unary - is done last) LDA #%101&3 ;= 1 LDA #>$1234+1 ;= $12 LDA #>$1234+256 ;= $13 LDA #^$123456 ;= $12 LDA #>$123456 ;= $34 (mid byte) LDA #1>3 ;= 0 (false) All of these expression types can be used in .BY statements as well. Like: LOWS .BY LABEL1 >LABEL2 >LABEL3 .BY 15+3!%1000 etc... - LABELS - The first character of a label may be any letter, or the symbols @, _, or ?. All remaining characters may also include numbers plus the '.' symbol. Labels may be up to 15 characters long. Label names, and for that matter all text entered with the assembler, can be entered in upper or lower case. Labels are not case-sensitive. When the first character of a label is '?', the label is a 'local label'. Locals are defined only in the source code segment between two global (i.e. non-local) labels. References to local labels cannot cross a global label definition. Internally, the assembler creates local labels by appending the local onto the end of the previous global label. Thus in the following code segment: DELAY LDX #100 ?L DEX BNE ?L '?L' is a local label, and will be entered in the symbol table as DELAY?L. Knowing how the label is stored, allows you to access it from the debugger or the Esc-V expression evaluator. You can also code a direct reference to the label DELAY?L if you need to access the local from the other side of the global label DELAY. Locals are not printed in X-reference or symbol table listings, which makes them very useful for simple loop and branch structures where you don't want to think up unique label names for all occurrences. - ADDRESSING MODES - All 6502 and 65816 addressing modes are supported. Any addresses that evaluate less than $100 will use zero page modes when possible. Thus, zero page labels must be defined before being used, or assembly errors will result. There is also a way to force 8 bit, 16 bit, or 24 bit addresses using the operators <, !, and >. (Yes, I know this is inconsistent with the immediate operators for low, high and bank bytes -- I didn't write the 65816 assembler specifications). This can be really useful for forcing absolute 16 bit addressing on zero page labels, to add 1 cycle in time critical applications. For the 65816, it can force direct page addressing for non-ZP labels, (which of course requires you to move the direct page register to the proper page address). ALL 24-bit addresses must be preceded by the > character. The operands for the 65816 MVP and MVN instructions should be simple bank bytes -- not full addresses. Ex: MVP $40 $80 moves memory from bank $40 to bank $80, using the addresses in X and Y. Or: MVP ^SRC ^DEST Use the bank byte of the source and destination addresses. - PSEUDO-OPS - Note that only 2 letters are required, but if additional letters are present they will be truncated without assembly errors. For example, you may use pseudo-ops like '.byte' and '.org'. Personally, I really like having the pseudo-ops the same width as all 6502 instructions, and only use 2 letters. .02 Set 6502-only mode. In this mode, all 65816-specific instructions will be flagged as "NOT 6502" errors. The code will still be assembled in these cases, however it will not run properly on 6502 based machines. .24 Sets the symbol table and program counter to use 24 bit addresses. This is only useful for 65816 programs, and may crash your machine if you try to use it without having a 65816 CPU. .816 Set 65816 mode, so that non-6502 instructions will not be flagged as errors. The initial setting of the .02 versus .816 assembly mode depends upon which processor version of MAE you are running. The opening menu display shows the current version number, and also an indication of whether it is a 6502 or 65816 version of the program. The initial assembly mode will match this. .AB The assembler will generate byte-sized values for accumulator-related immediate constants. (Default) .AW The assembler will generate word-sized values for accumulator-related immediate constants. This is only useful for 65816 programs. .BA byte For bank addressing, you can specify an operand to force assembler generated object code into bank select RAM. This byte will be stored into location $D301 when storing bytes of object code into RAM. .BI filename Includes the contents of a binary disk file into the assembly. If this file does not contain a DOS binary header, it will be assembled as in-line data at the current PC. Otherwise, a file that contains a header will be loaded at its load address. .BY [+byte] bytes and/or ASCII Store byte values in memory. ASCII strings can be specified by enclosing the string in either single or double quotes. If the first character of the operand field is a '+', then the following byte will be used as a constant and added to all remaining bytes of the instruction. Ex: .BY +$80 1 10 $10 'Hello' $9B will generate: 81 8A 90 C8 E5 EC EC EF 1B Values in .BY statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type. See also .SB which creates ATASCII screen codes, and .CB which creates strings in which the last byte is EOR'ed with $80. .CA byte This is to allow for assembly directly into a bank select cartridge environment. The byte is placed in the X register, and a STA $D500,X is performed when object code bytes are stored into memory. The only catch, is that the assembler needs to be able to return the bank select cartridge to normal. There is currently a 'STA $D5DC' for this purpose, but this may not be the right address for your cartridge setup. You should search the disk file for this instruction, ($8D $DC $D5), and replace it with the appropriate address. NOTE: The standard public version of MAE resides partially in the cartridge address space, and assuch this pseudo-op will not work properly. Custom versions of MAE that reside in different areas of system RAM, such as just above yourLOMEM, canbe provided upon request. .CB [+byte] bytes and/or ASCII This is in the same format as the .BY pseudo-op, except that the last character on the line will be EOR'ed with $80. .CL Close output object code file. When using the .OU pseudo-op to create object code files on disk, the file will normally be closed at the end of assembly. However, if you wish to close the file before that, it can be forced closed with the .CL pseudo-op. You may use this to create multiple output files in one assembly, or to place something in RAM in addition to the disk file. .DC word byte Define constant-filled block. This will fill an area of size 'word' with the constant 'byte'. .DS word Define storage. This will reserve an area of storage equal to size 'word'. .EC Do not display macro generated code in the assembly listing. Only the macro call itself will appear. .EL Used after a conditional .IF statement, this marks the "ELSE" portion of assembly. See the section on conditional assembly for more details. .EJ Eject -- Send a form feed code to eject the page in an assembly listing. .EN This is an optional pseudo-op to mark the end of assembly. It can be placed before the end of your source file to prevent a portion of it from being assembled. .EN can also be used to mark the end of a .IF conditional assembly section, (as in .ENDIF). Because pseudo-ops are only recognized to two characters, the .EN command will perform an ENDIF function when encountered within a conditiona assembly section, and will end the assembly otherwise. The "***" ENDIF operator used in pre-1.1 versions of MAE is still supported, and actually preferred since there is no ambiguity here. It is also a little more visible at the source level. .ES Display the object code resulting from Macro expansions. .FL floating point numbers Stores 6-byte BCD floating point numbers for use with the OS FP ROM routines. .HE hex bytes Store hex bytes in memory. This is a convenient method to enter strings of hex bytes, since it does not require the use of the '$' character. The bytes are still separated by spaces however, which I feel makes a much more readable layout than the 'all run together' form of hex statement that some other assemblers use. Example: .HE 0 55 AA FF .IB The assembler will generate byte-sized values for index register-related immediate constants. (Default) .IW The assembler will generate word-sized values for index register-related immediate constants. This is only useful for 65816 programs. .IF expression The expression will be evaluated, and if true, (non-zero), the statements following the .IF, up to a .EL or .EN (or ***) will be assembled. If the expression is false, then the block of statements will not be assembled. See the section on conditional assembly for more details. .IN filename Include additional files in the assembly. Only the main source file can contain .IN pseudo-ops. You cannot nest them. Default drive processing works the same here as it does when loading files from the editor, and so you will usually not need any 'Dn:' types of filespecs. The file name only should be sufficient. No quotes are needed either. .LC Turn off (clear) the display of the assembly listing. (Default) .LL Display the assembly listing on this line only, even if the full listing is turned off. This can be extremely handy to display the program counter value at important positions in the source file. .LO longwords Stores longwords, (3 byte values) in memory. .LS Turn on (set) the display of the assembly listing. .MC adr Move Code to a different address than the .OR assembly origin. If you are assembling to RAM, your code will be stored starting at the address after the .MC pseudo-op. When assembling to disk, the .MC address will be used when creating the binary file headers, affecting where the code will be loaded into. !!!name .MD Begin macro definition. Described in a separate section. .ME End macro definition. .MG Mark the current .IN include file as Macro Global. This keeps this file in memory throughout the assembly, which is required if the file contains macros that are referenced in other included files. .OC Turn off (clear) the storing of object code in memory. .OR adr Sets the origin address for the assembly. Note: If there is a label on this line, it will be given the value of the new origin. This is not the same as in Mac/65 which could use its origin directive to reserve space (*= *+1). You should use the .DS pseudo-op for reserving space. .OS Turn on (set) the storing of object code in memory. (Default) .OU filename Create an output disk file for the object code. Regretfully, this file is made up of individual 256 byte segments much like Mac/65 does. I apologize for the laziness here on my part, but it really was a lot easier to do this way. You should run some type of strip program to de-segment the file for optimal size and speed. The .OU pseudo-op should be placed above the .OR pseudo-op. .PR "text" Print a text message to the screen on pass 1 of the assembly. This is generally used with the .VA pseudo-op when prompting for values to be entered from the keyboard. .SB [+byte] bytes and/or ASCII This is in the same format as the .BY pseudo-op, except that it will convert all bytes into ATASCII screen codes before storing them. The ATASCII conversion is done before any constant is added with the '+' modifier. label .VA Will print a '?', and then accept input from the keyboard. You may enter any value, which will be given to the label in front of the .VA. .WO words Stores words in memory. Multiple words can be entered. SET label = expression Set the specified label to a new value. This instruction allows a label to be redefined with different values during the assembly. Any label can be SET. - CONDITIONAL ASSEMBLY - Conditional assembly allows the programmer to adapt the assembly process to different conditions. Blocks of code can be included or skipped over based upon the value of an expression. The format of conditional assembly is: .IF expression ; This block of code is assembled if the ;expression is true. .EL ; Else, this block of code gets assembled ;(when the expression is false) .EN ; Marks the end of the conditional block. The operand of the .IF instruction will be evaluated, and if the expression is true, then the source code following the .IF will be assembled until reaching a .EL or .EN pseudo-op. (Once again, two-letter pseudo-ops are a convenience, not a requirement. You are free to use the more standard .ELSE and .ENDIF if you prefer). The .EL portion is optional, and is used when you want one block of code to be assembled when the condition is true, and a different block when the condition is false. The end of the conditional is marked with either .EN or three asterisks ***. '***' is equivalent to an ENDIF statement, and is somewhat preferable since .EN will be interpretted as 'end of assembly' when it occurs outside of a valid conditional assembly block. '***' is non-ambiguous and will flag an assembly error if it does not have a matching .IF. Examples of Conditional Assembly: .IF FLAG . ;This block of code gets asm'ed . ;when FLAG <> 0 *** .IF FLAG=0 . ;This block does when FLAG = 0 *** .IF FLAG1!FLAG2 . ;asm'ed if FLAG1 or FLAG2 <> 0 *** .IF FLAG1^FLAG2 . ;asm'ed if FLAG1 . ;or FLAG2 <> 0, . ;but not both *** .IF WIDTH=40 . ;This gets asm'ed when width = 40 *** .IF WIDTH = 40 ;This is INVALID. Do not put spaces in expressions. ;Spaces separate expressions from each other. .IF WIDTH#40 . ;This gets asm'ed when width <> 40 *** .IF WIDTH<40 . ;asm'ed if WIDTH less than 40 .EL . ;asm'ed if WIDTH greater or equal to 40 *** .IF WIDTH>40 . ;if WIDTH greater than 40 *** - MACROS - Macros must be defined before they are used in your source. The definition looks like this: !!!name .MD ; body of the macro .ME Where 'name' is the name of the macro. The 3 exclamation marks are a special macro identifier, and must preceed the macro name. The body of the macro definition will follow, and should be ended with a .ME pseudo-op. The macro definition must be resident in memory when it is called. If you link multiple source files with .INclude pseudo-ops, then you need to ensure that any macro definitions are forced to be memory resident by using .MG within the file that contains the macros. Typically, you can put all your definitions in one file, put in the .MG option, and then include it at the beginning of your assembly. The root source file, that is, the one that is in memory when you issue the Esc-A assemble command, is always memory resident anyway and thus macros defined in your root source file are always available to other included files without the need for .MG. Beginning with MAE version 1.1, the assembler now has free-format and full text substitution macros. Macro parameters can be anything, and will be passed to the macro routine in their original text form. The number of parameters passed by the macro call is not rigidly enforced, and in fact the macro definition no longer has to specify the number of expected parameters. Within the body of the macro, parameters are accessed by using a ':' followed by a number from 1 to 9, corresponding to the order of parameters on the calling line. (Parameters are separated by spaces, and nothing else). A special macro parameter, ':0' can be used to get the actual number of parameters passed in. When a macro is expanded, any ':n' strings that are not within quotes will be replaced with the text from the calling line. Text within quotes will normally be left as-is, which means there needs to be a special method of getting a macro parameter expanded inside of quote marks. Two double quotes in a row, "", will be replaced with one double quote, and subsequent macro parameters will be expanded. Then use another set of two double quotes to close. See the macro examples below for more details on how this works. An individual macro may pass up to 9 parameters, but there is also a limit on the total number of parameters including all nesting levels. This limit is 16. If a macro uses 8 parameters, then any nested macros it calls can use at most 8 additional parameters. Any labels defined within a macro must use a special form. Because macros can be expanded multiple times, a special label type exists to avoid errors from multiple label definitions. These label types start with three periods, followed by any normal label name. These special macro labels will be given unique numbers with each macro expansion to keep them separate. You can consider them local labels to each macro expansion. Here's an example of a macro to increment a two byte value: !!!IND .MD INC :1 BNE ...SKP INC 1+:1 ...SKP .ME To call this macro, you would use: IND $80 Since a macro call will pass any text characters, you could call the same macro with: IND $80,X Note that the structure of the second INC instruction in the macro body is important here for this to work correctly. If the line were written "INC :1+1", it would get expanded to "INC $80,X+1" which is not valid. Here are more examples that don't do anything specific code-wise, but serve to demonstrate various macro techniques. The calling line will be listed first, followed by the definition and what it will actually be expanded to. PRT "HELLO" $9B "THERE" $9B !!!PRT .MD JSR PRINT JSR PRINT .BY :1 :2 :3 :4 :5 :6 :7 :8 0 .BY "HELLO" $9B "THERE" $9B 0 .ME Parameters that are not defined on the calling line are simply replaced with null strings. They do got generate errors. The PRINT subroutine in this example would pull the return address off the stack, display the string that it points to until 0 is reached, and then push that address back on the stack so program flow continues with the next line of code. TST $80 !!!TST .MD .BY :1 .BY $80 ;direct substitution .BY ":1" .BY ":1" ;strings inside quotes are not expanded .BY "":1"" .BY "$80" ;two quotes get converted to one, and the ;parameter gets expanded since it is not ;between quote marks. Here's a more complicated example that can be used as a debugging aide during development. ASSERT INDEX CC #$80 !!!ASSERT .MD .IF DEBUG PHP PHP PHA PHA LDA :1 LDA INDEX CMP :3 CMP #$80 B:2 ...OK BCC ...OK JSR PRINT JSR PRINT .BY "Assert Failed: " "":1 :2 :3"" 0 .BY "Assert Failed: " "INDEX CC $80" 0 ...OK PLA PLP *** The idea behind the ASSERT macro, is that it can be used to verify the value of key variables, notify the programmer when the value is not in range, and all the code disappears when you assemble the final version simply by setting the DEBUG flag to 0. There are more macro examples in the supplied include file MACROS. If you createsome really useful macros, please send them to me and let me know if they can be included in future MAE distributions. - ERROR MESSAGES - These are the error messages that can be produced by the assembler. Error messages are marked with an '!', and also include the source line number that they occurred on. If you are assembling a single file, or if the errors occurs in your main file, you will be able to use the editor ^H command to jump directly to the errors. For errors that occur in included files, you will need to load in that file, and jump to those line numbers manually using the ^G goto line number command. BRANCH Branch instruction out of range. OPCODE Error in opcode field. This can be either a bad 65816 instruction, bad pseudo-op, or an undefined macro. DUP Duplicate label definition. EOF End of File error. All assemblies must end with a .EN pseudo-op. This should be in the main source file, not in any included files. This error can also occur if a conditional or macro definition is pending at the .EN. UNDEF Undefined label reference. NEST Nesting error. .MD macro definitions cannot contain additional definitions. .IN included files may not include additional .IN files. Endif "***" mark without associated .IF statement. OPERAND Error in operand field. ADR MODE Addressing mode not supported. BAD LABEL Bad characters in label name. MACRO OV Macro overflow in either the number of expansions, or level of nested expansions. SYM OV Symbol table overflow. PARMS Number of macro parameters in the call does not match the definition. LABEL MISSING Missing label on either a SET pseudo-op or in an = equate definition. NOT 6502 This instruction is only valid on 65816 processors, and will not run on 6502-based computers. This error is only generated if you have used the .02 pseudo-op to generate 6502-only code.