EZAsm Version 1.31 December '90 by Joe Siebenmann DISCLAIMER: You have the right to freely use, copy and distribute this program as long as the following conditions are met: 1. The program and documentation are not modified in any way. 2. The program is not used or included in any package for profit unless written consent from the author is obtained. NOTE: The author does not accept any responsibility for any damage that might result from the use of this program. EZAsm was written to make programming in 68000 assembly language much easier, for both assembly language programmers, and those who may be interested in using the speed and compact code that assembly language provides for a project. EZAsm combines parts of the "C" language with 68000 assembly, giving it the "feel" of a higher level language. The resulting code is optimized as much as possible. Here are some of its advantages: - It always converts your statement into the fastest possible assembly statement(s), so you automatically write "good" code. - More structured. Compare, and bit test statements can have braces and "else" like "C". Being able to use braces lets you use assembly in a whole new way! - "C" like Amiga function calls! Every 1.3 function in every library is supported. An XREF table is automatically built and inserted. - Makes code MUCH more readable, for debugging etc. - Enables you to code nearly TWICE as fast, with fewer syntax errors. - No more having to constantly look up which condition code to use for compares, bit tests, or maximum numbers for "moveq" or "addq" etc. - You can insert normal assembly statements anywhere, for your special needs. You need to know a little about assembly language, and "C" operators, before you dive right in. If you're new to 68000 assembly language, I suggest looking at one of the many available books on the subject. The included example source programs can give you a good idea about: - General statement syntax. - Use of the additional arguments, to "force" size etc. - Using assembly statements in your code. **************************************** Using EZAsm: EZAsm filename.s Where filename is the name of your source file including any path. Give your source file an extension ( .s etc. ) or your linker will overwrite it. It produces an output file called filename.asm . Be sure the files "funcnm" & "funcdat" are in the same directory as EZAsm. Cd into this directory and you're ready to go. Depending on which assembler you use on the final output file, you may need to change the extension from .asm to .a or make minor changes or additions to your source. The code section has been made as compatible as possible. If your assembler complains, it's typically something in the data section. Manx: as scroll.asm ln scroll.o -lc ********************************* * Operand Table * ********************************* Operand Type Mode [A] [B] [C] [D] [E] [F] Dn * * * - * - An * - * - - - (An) * * * * * * (An)+ * * * * * * -(An) * * * * * * d(An) * * * * * * d(An,Xn) * * * * * * abs.w * * * * * * abs.l * * * * * * d(PC) * - - - * * d(PC,Xn) * - - - * * d (immed) * - - - - - declared variables: "foo" becomes "foo(a5)" ( d(An) ) examples: abs.w: ($1f40) (1024) CLR_PUB *1 abs.l: ($bfe001) (12574721) Max *1 d (immd): $dff000 -1 512 *1 Where these have been previously defined as "CLR_PUB equ $10001" etc. ************************************** ( decimal or hex ) #<1> 1 - 8 #<2> 0 - 7 #<3> 0 - 31 #<4> -128 - 127 # any byte, word, or long size number Dn d0 - d7 An a0 - a7 {B} B (byte) data size not allowed for An operands ***************************************** * legal * converted * legal * * operands * to * sizes * ***************************************** Addition Subtraction ++ -- [C] addq/subq L,W,{B} += -= Dn [A] add/sub L,W,B [D] Dn add/sub L,W,B An [A] adda/suba L,W [B] # addi/subi L,W,B [C] #<1> addq/subq L,W,{B} Examples: Total ++ d1 += 10 Optional Args: l, w, b ********************************** Multiplication Division *= /= Dn [E] divu/mulu (default) W Dn [E] s divs/muls W ( SPECIAL: Where # is a byte or word length number. ( higher could overshift the result ) The resulting code is larger then "mulu" or "muls" but will execute much faster. ( it will default to a normal "mulu" or "muls" if the number isn't "right" ) ) Dn *= # L Examples: d0 *= d1 d2 /= 2 Optional Args: w (default), s ********************************** And Or Exclusive Or &= |= [B] # andi/ori L,W,B Dn [E] and/or L,W,B [D] Dn and/or L,W,B x= [B] Dn eor L,W,B [B] # eori L,W,B Examples: Mask &= %11010000 Flags |= $f0 Optional Args: l, w, b *********************************** Shift Left/Right << >> Dn Dn lsl/lsr L,W,B Dn 1-8 lsl/lsr L,W,B [D] 1 lsl/lsr L,W,B a asl/asr ( SPECIAL: Normally you're limited to 1-8 and must use a data register to hold higher. In this statement the output is optimized, so it's faster, and saves using a data register! ( logical only ) ) Dn 1-31 L Examples: d2 << d0 d1 >> 4 Optional Args: l, w, b, a ( logical is default ) *********************************** Assign = [B] [A] move L,W,{B} An [A] movea L,W Dn #<4> moveq L Examples: temp = Total (a1)+ = 0 w Optional Args: l, w, b *********************************** Compare >= <= != > < = Dn [A] cmp L,W,{B} An [A] cmpa L,W [B] # cmpi L,W,B (An)+ (An)+ cmpm L,W,B Syntax types: [opr] [op] [opr] label [opr] [op] [opr] { . . } [opr] [op] [opr] { . . } else { . . } Example: Total >= 100 Over Optional Args: ( placed AFTER label or brace ) l, w, b, s ******************************************** Bit test Dn:0-31 = 0-1 label btst.l L Dn:Dn = 0-1 label L [F]:0-7 = 0-1 label btst.b B [F]:Dn = 0-1 label B ( You can also use braces "{" instead of a label, see compare for use of braces ) Examples: d1:0 = 1 CalcRtn ($bfe001):6 = 0 LMBDown d2:d0 = 1 ItsSet Optional Args: ( ignored ) Rules: - No spaces inside first operand. - Only "=" is allowed. - Right operand can only be 0 or 1. **************************************** Additional Arguments: b forces operation to be byte w " " " word l " " " long a arithmetic shift ( <<, >> ) ( logical is default ) s signed ( *=, /= , compares ) ( these are RESERVED and can't be used as variables, or labels ( unless you use upper case ) ) ****************************************************** Functions: Syntax types: CloseWindow( Window ) Buf = AllocMem( 512 $10001 ) Permit( ) Example: -------------------------------- dosname dc.b "dos.library",0 fname dc.b "df0:myfile",0 CLEAR_PUBLIC equ $10001 OLD equ 1005 LONG _DosBase FHandle num Rbuf _DosBase = OpenLibrary( #dosname 0 ) beq Exit Rbuf = AllocMem( 100 #CLEAR_PUBLIC ) beq Exit FHandle = Open( #fname #OLD ) beq Exit d3 = 100 ; preload D3 *1 num = Read( d0 Rbuf * ) . . . *1 normally you wouldn't need to do this, it's only an example.. --------------------------------- - You must first do an OpenLibrary( ) to access functions in a library. Functions in ConsoleDevice are accessed differently. See the ConsoleDevice section in the RKM p 662. ( load the io_Device field into "_ConBase" ) Functions in ExecBase are always accessible and don't need to be opened. The library bases MUST be named: _ConBase _DiskfontBase _DosBase _ExpansionBase _GfxBase _IconBase _IntuitionBase _LayersBase _MathBase _MathIeeeDoubBasBase _MathIeeeDoubTransBase _MathTransBase _PotgoBase _RomBootBase _TimerBase _TranslatorBase - Unfortunately the leading underscores are necessary so you can use includes without your assembler complaining. Some library bases ( IntuitionBase, ExecBase, GfxBase, ExpansionBase, RomBootBase ) are already defined in some .i's resulting in "multiply defined symbol" errors. - You might notice that ExecBase is missing. It isn't needed. ( it's loaded with "movea.l $4,a6" ) Permit( ) OpenLibrary( #dosname 0 ) ^ ^ ^ no space | | space | space - The function name must be followed immediately with "(" ( no spaces between ) and then followed by a space or tab. If the function has no arguments, it still needs a space in the middle. Arguments must be separated with a space or tab. Arguments: - If your argument is already in a DIFFERENT data or address register, you can pass it as an argument. Often a previous function will put results in D0, so just pass d0. If the proper register is already loaded just pass "*". The above example shows both of these. ( if you pass it the same register it uses, it'll be skipped. func defined as: Lock( D1 D2 ) "Flock = Lock( d1 -2 )" arg "d1" will be skipped ) - If you need a pointer to a newwindow, filename, library name, etc. use "#dosname" etc. like the example above. It'll load its address ( pointer ) into a data or address register. All functions within these ( 1.3 ) libraries are supported: ConsoleDevice DiskfontBase DOSBase ExecBase ( SysBase ) ExpansionBase GfxBase IconBase IntuitionBase LayersBase MathBase MathIeeeDouBasBase MathIeeeDoubTransBase MathTransBase PotgoBase RomBootBase TimerBase TranslatorBase - It keeps track of the current library base. As long as no user labels, or close braces ("}") are hit, it will not re-load the base register for functions which have the same library base. - The file "funcnm" contains a list of every function that is supported. ************************************************** Optimizations: STATEMENT BECOMES NOTE An = 0 sub.l An,An [B] = 0 clr [B] 1 ( compares ) [B] = 0 [B] != 0 tst [B] bcc label ---------------------------------- [B] += # [B] -= # [B] &= # [B] |= # [B] x= # [B] = # moveq #,d7 2 [opr].l d7,[B] ( compares ) dn .. # label moveq #,d7 2 cmp.l d7,dn bcc label an .. # label moveq #,d7 2 cmpa.l d7,an bcc label ---------------------------------- Dn << 1-31 Dn >> 1-31 3, 2 An += #1 lea n(An),An An -= #1 lea -n(An),An 4, 2 An = #2 lea n,An 5, 2 Dn *= #1 6 # = 1-127 ( #<4> if appropriate ) #1 = any byte or word length number #2 = any byte, word, or long length number Notes: 1 For byte and word sizes the code size is smaller, for long, its smaller and faster. ( then "move #0,[B]" ) 2 Only apply to long sized operations. 3 The resulting instructions are combinations of "swap", "clr.w", "add.l", "lsr" or "lsl". 4 ( 1-8 handled by "addq", "subq" ) 5 ( 0 handled by "sub.l An,An" ) 6 The resulting instructions are combinations of "move.l", "asl.l", "asr.l", "add.l". ******************************************** General Info: - Statements can be indented as you like. Operands, operator, and arguments must be separated by at least one space or tab. - Braces can be nested up to 30 deep. - Only one statement per line. - If you declare variables or use function call's, the first statement in your code must be an "EZAsm statement" so it knows when to insert the XREF's and/or get stack frame for the variables. ( comments or assembly directives placed between your variables and the start of your code will be out of place in the output file ) - Comments at 1st column must begin with "*" or ";". Comments after statements must be begin with ";" and be separated from last argument, or operand. ( both types are transferred to output file ( some may not )) - Operands supported: octal: @141 hex: $61 binary: %1100001 ASCII: 'a' and decimal: 97 ASCII strings in operands can contain a maximum of 4 characters. ( no quotes within quotes permitted ) - To make labels and symbols as compatible as possible, they arn't checked for illegal characters. Typically the 1st character must be a letter, underscore "_", or a period "." . The rest of the characters can be any of these plus 0-9. - Labels and symbols can have an unlimited length. ( check your assembler to find what length they are significant to ( usually around 30 ) ) - Labels that don't begin at column 1 should be followed immediately with ":". - Local labels ( "2$ d1 = 0" ) are supported. - No need to put in the usual "moveq #0,d0", "rts" at the end of your code, its part of the closing block of code it inserts to free the variables. - D7 is used as a scratch register for some optimizations, so be careful if you use it. - Labels that it generates for braces are in the range ".laaa" to ".lzzz" so try to avoid using them. ( upper case is OK ) - "SP", "PC", "CCR", "SR" & "USP" are supported, but you must ensure the size is legal as no checks are made. ( must be upper case ) - For best viewing of output file set your tabs to 8 spaces. ( printing should be fine ) IMPORTANT! : Use of register A5 is RESERVED! ( it contains the base address for variable storage ) ******************************** Variable Declaration: LONG foo Save[10] bar ... WORD DMASave ... BYTE Sw ... - "xxx[n]" reserves n consecutive blocks of given size, with "xxx" pointing to 1st byte. - You're limited to 14 variables per line. - No other statement types may occur between these lines. - Variables must be separated by at least one space, or tab. - To keep things word aligned, if an odd number of bytes are declared in BYTE, an extra byte will be added. This will offset the next "equ" by one. ( in the case of "BYTE Count foo buf[3]" ( 5 bytes ) an extra would be added ) - Variables are stored on the stack, and are not cleared. Be sure to properly initialize them. - Any number of these can be used, in any order. - Must occur JUST BEFORE your program code. - Must begin at 1st column, with LONG, WORD, or BYTE in upper case. ************************************************ How to get what you need, hints, etc.: - Using "ENDS": Its safer to put all your data AFTER your code so it can't accidentally be executed when it runs. To do this, put "ENDS" ( END Source ) after your code, ( signals the closing block of code to free the variables to be output ) and "END" at the very bottom. "END" must allways be at the bottom. ( see the example source "window.s" which uses this ) - Instruction size: In most cases you won't need a size argument. It knows the size of the variables, address and data registers, and is smart enough to know what size to use. It determines the size of data by it's actual value not its physical size. ( $0020 %11010000 $12 125 are all BYTE size ) If the data is smaller than the instruction size you want, ( d1 = $20 w ) or it can't know an operands size ( (a2)+ = 3(a0) l ) you'll need to give it a size argument. Caution: Be aware that if you load small variables into larger ones, the upper bytes will not be cleared and may garbage your result. - Since "(a1)" refers to the CONTENTS of the byte, word, or long that a1 points to, "($dff180)" is used in a similar way. "move.w $dff01e,d0" would become, "d0 = ($dff01e) w". ( decimal addr's are also valid: "(4)", "(256)" ) - When numbers are used as operands "$fe02" or "37", they are converted to "#$fe02", "#37". ( when "$2f(a2)" or "15(a0)" are used, they are left AS IS ( see below )) - Operands that it DOESN'T RECOGNIZE, doesn't match with any declared LONG, WORD, BYTE, or standard "(a1)+" etc., get output AS IS. This is VERY USEFUL when you need operands like: "#intuiname", "wd_UserPort(a1)", "$20(a1)", "DMACON(a6)" - Most statements set condition code flags on the result of the operation. Often, instead of using a compare to check the result, you can use an assembly instruction, branching on the state of a condition code flag! ( you need to check what flags are set ( if any ) for an instruction ) - If at any time you're unsure of what a statement is being output as, or want to check something out, just look at the output file. - I think it's a good idea to get away from using include files. It speeds up the assembler tremendously. Most assembly source files I see do this. **************************************** Errors: "Illegal argument" The argument found was not valid for the operator. See the list of "Optional Args" for the operator. It must be lower case, and be separated from the operands by at least a space or a tab. "Illegal operand" One, or both, of the operands are: not valid for the operator, have an invalid number, or byte size was specified for an "An" operand {B}. In most cases it's looking for "Dn" or "An" as one of the operands. ( look under the "legal operands" of the operator for a valid combination ) "Illegal size" The argument size you specified is not valid for the operator. Check the "legal sizes" for the operator. "Needs size argument" It doesn't have enough size information about the operands to calculate an instruction size. You need to add an l, w, or b argument. "Label not found" No label matching your label argument was found. "Brace mismatch" Checks are made when a closing brace ( "}" ) is hit, and when "END" is hit. If the brace stack is "messed up" at that time, an error is given. If "}" is shown, look from there up. Both "}" and "END" may appear. If just "END", look for a "{" or "} else {" without a matching "}". "Function not found" No function matching your function name was found. Check case and spelling of function name, and be sure there isn't a space before the "(". Check the list of supported function names in the file "funcnm". "Function argument count incorrect" Check the number of arguments you used for the function. Too many or not enough were used. ******************************************************************* ******************************************************************* Changes for 1.31 : o New "funcdat". ( same size ) o Instruction size logic has been improved, now with no warnings. Variable's "equ"'s have been changed. o Use of "$fffffffe" etc. now optimized for "moveq". I hope you find this program useful. If you have any ideas for improvements, bugs, or something you'd like to see in a future version, I would appreciate hearing from you! I can be contacted at: ( till around spring of '91 ) Joe Siebenmann 8303 Old Tree Ct. Springfield, VA 22153 (703) 455-4982 ( anytime ) PLINK: IGZ798 Happy Programming!