Macro Assembler AS V1.42
User's Manual
Edition July 2019
IBM, PPC403Gx, OS/2, and PowerPC are registered trademarks of IBM Corporation.
Intel, MCS-48, MCS-51, MCS-251, MCS-96, MCS-196 und MCS-296 are registered trademarks of Intel Corp. .
Motorola and ColdFire are registered trademarks of Motorola Inc. .
MagniV is a registered trademark of Freescale Semiconductor.
PicoBlaze is a registered trademark of Xilinx Inc.
UNIX is a registered trademark of the The Open Group.
Linux is a registered trademark of Linus Thorvalds.
Microsoft, Windows, and MS-DOS are registered trademarks of Microsoft Corporation.
All other trademarks not explicitly mentioned in this section and used in this manual are properties of their respective owners.
This document has been processed with the LaTeX typesetting system, using the Linux operating system.
This instruction is meant for those people who are already very familiar with Assembler and who like to know how to work with AS. It is rather a reference than a user's manual and so it neither tries to explain the ''language assembler'' nor the processors. I have listed further literature in the bibliography which was substantial in the implementation of the different code generators. There is no book I know where you can learn Assembler from the start, so I generally learned this by ''trial and error''.
Before we can go ''in medias res'', first of all the inevitable prologue:
As in the present version is licensed according to the Gnu General Public License (GPL); the details of the GPL may be read in the file COPYING bundled with this distribution. If you did not get it with AS, complain to the one you got AS from!
Shortly said, the GPL covers the following points:
To accelerate the error diagnose and correction, please add the following details to the bug report:
Please don't call me by phone. First, complex relations are extremely hard to discuss at phone. Secondly, the telephone companies are already rich enough...
The latest version of AS (DPMI, Win32, C) is available from the following Server:
http://john.ccac.rwth-aachen.de:8000/asor shortly
http://www.alfsembler.de
Whoever has no access to an FTP-Server can ask me to send the assembler by mail. Only requests containing a blank CD-R and a self-addressed, (correctly) stamped envelope will be answered. Don't send any money!
Now, after this inevitable introduction we can turn to the actual documentation:
In contrast to ordinary assemblers, AS offers the possibility to generate code for totally different processors. At the moment, the following processor families have been implemented:
The reason for this flexibility is that AS has a history, which may also be recognized by looking at the version number. AS was created as an extension of a macro assembler for the 68000 family. On special request, I extended the original assembler so that it was able to translate 8051 mnemonics. On this way (decline ?!) from the 68000 to 8051, some other processors were created as by-products. All others were added over time due to user requests. So At least for the processor-independent core of AS, one may assume that it is well-tested and free of obvious bugs. However, I often do not have the chance to test a new code generator in practice (due to lack of appropriate hardware), so surprises are not impossible when working with new features. You see, the things stated in section 1.1 have a reason...
This flexibility implies a somewhat exotic code format, therefore I added some tools to work with it. Their description can be found in chapter 6.
AS is a macro assembler, which means that the programmer has the possibility to define new ''commands'' by means of macros. Additionally it masters conditional assembling. Labels inside macros are automatically processed as being local.
For the assembler, symbols may have either integer, string or floating point values. These will be stored - like interim values in formulas - with a width of 32 bits for integer values, 80 or 64 bits for floating point values, and 255 characters for strings. For a couple of micro controllers, there is the possibility to classify symbols by segmentation. So the assembler has a (limited) possibility to recognize accesses to wrong address spaces.
The assembler does not know explicit limits in the nesting depth of include files or macros; a limit is only given by the program stack restricting the recursion depth. Nor is there a limit for the symbol length, which is only restricted by the maximum line length.
From version 1.38 on, AS is a multipass-assembler. This pompous term means no more than the fact that the number of passes through the source code need not be exactly two. If the source code does not contain any forward references, AS needs only one pass. In case AS recognizes in the second pass that it must use a shorter or longer instruction coding, it needs a third (fourth, fifth...) pass to process all symbol references correctly. There is nothing more behind the term ''multipass'', so it will not be used further more in this documentation.
After so much praise a bitter pill: AS cannot generate linkable code. An extension with a linker needs considerable effort and is not planned at the moment.
Those who want to take a look at the sources of AS can simply get the Unix version of AS, which comes as source for self-compiling. The sources are definitely not in a format that is targeted at easy understanding - the original Pascal version still raises its head at a couple of places, and I do not share a couple of common opinions about 'good' C coding.
Though AS started as a pure DOS program, there are a couple of versions available that are able to exploit a bit more than the Real Mode of an Intel CPU. Their usage is kept as compatible to the DOS version as possible, but there are of course differences concerning installation and embedding into the operating system in question. Sections in this manual that are only valid for a specific version of AS are marked with a corresponding sidemark (at this paragraph for the DOS version) aheaded to the paragraph. In detail, the following further versions exist (distributed as separate packages):
In case you runinto memory problems when assembling large and complex programs under DOS, there is a DOS version that runs in protected mode via a DOS extender and can therefore make use of the whole extended memory of an AT. The assembly becomes significantly slower by the extender, but at least it works...
There is a native OS/2 version of AS for friends of IBM's OS/2 operating system. Since version 1.41r8, this is a full 32-bit OS/2 application, which of course means that OS/2 2.x and at least an 80386 CPU are mandatory.
You can leave the area of PCs-only with the C version of AS that was designed to be compilable on a large number of UNIX systems (this includes OS/2 with the emx compiler) without too much of tweaking. In contrast to the previously mentioned versions, the C version is delivered in source code, i.e. one has to create the binaries by oneself using a C compiler. This is by far the simpler way (for me) than providing a dozen of precompiled binaries for machines I sometimes only have limited access to...
Scotty: Captain, we din' can reference it!
Kirk: Analysis, Mr. Spock?
Spock: Captain, it doesn't appear in the symbol table.
Kirk: Then it's of external origin?
Spock: Affirmative.
Kirk: Mr. Sulu, go to pass two.
Sulu: Aye aye, sir, going to pass two.
The hardware requirements of AS vary substantially from version to version:
The DOS version will principally run on any IBM-compatible PC, ranging from a PC/XT with 4-dot-little megahertz up to a Pentium. However, similar to other programs, the fun using AS increases the better your hardware is. An XT user without a hard drive will probably have significant trouble placing the overlay file on a floppy because it is larger than 500 Kbytes...the PC should therefore have at least a hard drive, allowing acceptable loading times. AS is not very advanced in its main memory needs: the program itself allocates less than 300 Kbytes main memory, AS should therefore work on machines with at least 512 Kbytes of memory.
The version of AS compiled for the DOS Protected Mode Interface (DPMI) requires at least 1 Mbyte of free extended memory. A total memory capacity of at least 2 Mbytes is therefore the absolute minimum given one does not have other tools in the XMS (like disk caches, RAM disks, or a hi-loaded DOS); the needs will rise then appropriately. If one uses the DPMI version in a DOS box of OS/2, one has to assure that DPMI has been enabled via the box's DOS settings (set to on or auto) and that a sufficient amount of XMS memory has been assigned to the box. The virtual memory management of OS/2 will free you from thinking about the amount of free real memory.
The hardware requirements of the OS/2 version mainly result from the needs of the underlying operating system, i.e. at minimum an 80386SX processor, 8 Mbytes of RAM (resp. 4 Mbytes without the graphical user interface) and 100..150 Mbytes of hard disk space. AS2 is only a 16-bit application and therefore it should also work on older OS/2 versions (thereby reducing the processor needs to at least an 80286 processor); I had however no chance to test this.
The C version of AS is delivered as source code and therefore requires a UNIX or OS/2 system equipped with a C compiler. The compiler has to fulfill the ANSI standard (GNU-C for example is ANSI-compliant). You can look up in the README file whether your UNIX system has already been tested so that the necessary definitions have been made. You should reserve about 15 Mbytes of free hard disk space for compilation; this value (and the amount needed after compilation to store the compiled programs) strongly differs from system to system, so you should take this value only as a rough approximation.
Principally, you can obtain AS in one of two forms: as a binary or a source distribution. In case of a binary distribution, one gets AS, the accomanying tools and auxiliary files readily compiled, so you can immediately start to use it after unpacking the archive to the desired destination on your hard drive. Binary distibutions are made for widespread platforms, where either the majority of users does not have a compiler or the compilation is tricky (currently, this includes DOS and OS/2). A source distribution in contrast contains the complete set of C sources to generate AS; it is ultimately a snapshot of the source tree I use for development on AS. The generation of AS from the sources and their structure is described in detail in appendix I, which is why at this place, only the contents and installation of a binary distribution will be described:
The contents of the archive is separated into several subdirectories, therefore you get a directory subtree immediately after unpacking without having to sort out things manually. The individual directories contain the following groups of files:
File | function |
---|---|
Directory BIN | |
AS.EXE PLIST.EXE PBIND.EXE P2HEX.EXE P2BIN.EXE AS.MSG PLIST.MSG PBIND.MSG P2HEX.MSG P2BIN.MSG TOOLS.MSG CMDARG.MSG IOERRS.MSG |
executable of assembler lists contents of code files merges code files converts code files to hex files converts code files to binary files text resources for AS text resources for PLIST text resources for PBIND text resources for P2HEX text resources for P2BIN common text resources for all tools common text resources for all programs |
Directory DOC | |
AS_DE.DOC AS_DE.HTML AS_DE.TEX AS_EN.DOC AS_EN.HTML AS_EN.TEX |
german documentation, ASCII format german documentation, HTML format german documentation, LaTeX format english documentation, ASCII format english documentation, HTML format english documentation, LaTeX format |
Directory INCLUDE | |
BITFUNCS.INC CTYPE.INC 80C50X.INC 80C552.INC H8_3048.INC REG166.INC REG251.INC REG29K.INC |
functions for bit manipulation functions for classification of characters register addresses SAB C50x register addresses 80C552 register addresses H8/3048 addresses and instruction macros 80C166/167 addresses and bits 80C251 peripheral addresses AMD 2924x |
File | Function |
---|---|
Directory INCLUDE | |
REG53X.INC REG6303.INC REG683XX.INC REG7000.INC REG78310.INC REG78K0.INC REG96.INC REGACE.INC REGF8.INC REGAVROLD.INC REGAVR.INC REGCOLD.INC REGCOP8.INC REGGP32.INC REGH16.INC REGHC12.INC REGM16C.INC REGMSP.INC REGS12Z.INC REGST6.INC REGST7.INC REGSTM8.INC REGST9.INC REGZ380.INC STDDEF04.INC STDDEF16.INC STDDEF17.INC STDDEF18.INC STDDEF2X.INC STDDEF37.INC STDDEF3X.INC |
register addresses H8/53x register addresses 6303 register addresses 68332/68340/68360 register addresses TMS70Cxx register addresses & vectors 78K3 register addresses 78K0 register addresses MCS-96 register addresses ACE register and memory addresses F8 register and bit addresses AVR family (old) register and bit addresses AVR family register and bit addresses Coldfire family register addresses COP8 register addresses 68HC908GP32 register addresses H16 register addresses 68HC12 register addresses Mitsubishi M16C register addresses TI MSP430 register and bit addresses S12Z family register and macro definitions ST6 register and macro definitions ST7 register and macro definitions STM8 register and macro definitions ST9 register addresses Z380 register addresses 6804 instruction macros and register addresses PIC16C5x register addresses PIC17C4x register addresses PIC16C8x register addresses TMS3202x register and bit addresses TMS370xxx peripheral addresses TMS320C3x |
File | Function |
---|---|
Directory INCLUDE | |
STDDEF4X.INC STDDEF47.INC STDDEF51.INC STDDEF56K.INC STDDEF5X.INC STDDEF60.INC REGSX20.INC AVR/*.INC COLDFIRE/*.INC S12Z/*.INC ST6/*.INC ST7/*.INC STM8/*.INC STDDEF62.INC STDDEF75.INC STDDEF87.INC STDDEF90.INC STDDEF96.INC STDDEFXA.INC STDDEFZ8.INC |
peripheral addresses TMS320C4x instruction macros TLCS-47 definition of SFRs and bits for 8051/8052/80515 register addresses DSP56000 peripheral addresses TMS320C5x instruction macros and register addresses PowerPC register and bit addresses Parallax SX20/28 register and bit addresses AVR family (do not include directly, use REGAVR.INC) register and bit addresses ColDfire family (do not include directly, use REGCOLD.INC) register and bit addresses S12Z family (do not include directly, use REGS12Z.INC) register and bit addresses ST6 family (do not include directly, use REGST6.INC) register and bit addresses ST7 family (do not include directly, use REGST7.INC) register and bit addresses STM8 family (do not include directly, use REGSTM8.INC) register addresses and macros ST6 (old) register addresses 75K0 register and memory addresses TLCS-870 register and memory addresses TLCS-90 register and memory addresses TLCS-900 SFR and bit addresses Philips XA register addresses Z8 family |
Directory LIB |
Depending on the platform, a binary distribution however may contain more files to allow operation, like files necessary for DOS extenders. In case of the DOS DPMI version, the extensions listed in table 2.4 result. Just to mention it: it is perfectly O.K. to replace the tools with their counterparts from a DOS binary distribution; on the on hand, they execute significantly faster without the extender's overhead, and on the other hand, they do not need the extended memory provided by the extender.
File | Function |
---|---|
Directory MAN | |
ASL.1 PLIST.1 PBIND.1 P2HEX.1 P2BIN.1 |
quick reference for AS quick reference for PLIST quick reference for PBIND quick reference for P2HEX quick reference for P2BIN |
Directory BIN | |
DPMI16BI.OVL RTM.EXE |
DPMI server for the assembler runtime module of the extender |
An OS/2 binary distribution contains in addition to the base files a set of DLLs belonging to the runtime environment of the emx compiler used to build AS (table 2.5). In case you already have these DLLs (or newer versions of them), you may delete these and use your ones insted.
File | function |
---|---|
Directory BIN | |
EMX.DLL EMXIO.DLL EMXLIBC.DLL EMXWRAP.DLL |
runtime libraries for AS and its tools |
There is no need for a special installation prior to usage of AS. It is sufficient to unpack the archive in a fitting place and to add a few minor settings. For example, this is an installation a user used to UNIX-like operating systems might choose:
Create a directory c:\as an (I will assume in the following that you are going to install AS on drive C), change to this directory and unpack the archiv, keeping the path names stored in the archive (when using PKUNZIP, the command line option -d is necessary for that). You now should have the following directory tree:
c:\as c:\as\bin c:\as\include c:\as\lib c:\as\man c:\as\doc c:\as\demosNow, append the directory c:\as\bin to the PATH statement in your AUTOEXEC.BAT, which allows the system to find AS and its tools. With your favourite text editor, create a file named AS.RC in the lib directory with the following contents:
-i c:\as\includeThis so-called key file tells AS where to search for its include files. The following statement must be added to your AUTOEXEC.BAT to tell AS to read this file:
set ASCMD=@c:\as\lib\as.rcThere are many more things you can preset via the key file; they are listed in the following section.
The installation of the DPMI version should principally take the same course as for the pure DOS version; as soon as the PATH contains the bin directory, the DOS extender's files will be found automatically and you should not notice anything of this mechanism (except for the longer startup time...). When working on an 80286-based computer, it is theoretically possible tha you get confronted with the following message upon the first start:
machine not in database (run DPMIINST)Since the DPMIINST tool ins not any more included in newer versions of Borland's DOS extender, I suppose that this is not an item any more...in case you run into this, contact me!
The installation of the OS/2 version can generally be done just like for the DOS version, with the addition that the DLLs have to be made visible for the operating system. In case you do not want to extend the LIBPATH entry in your CONFIG.SYS, it is of course also valid to move the DLLs into a directory already listed in LIBPATH.
As already mentioned, the installation instructions in this section limit themselves to binary distributions. Since an installation under Unix is currently alway a source-based installation, the only hint I can give here is a reference to appendix I.
AS is a command line driven program, i.e. all parameters and file options are to be given in the command line.
A couple of message files belongs to AS (recognizable by their suffix MSG) AS accesses to dynamically load the messages appropriate for the national language. AS searches the following directories for these files:
The language selection (currently only German and English) is based on the COUNTRY setting under DOS and OS/2 respectively on the LANG environment variable under Unix.
In order to fulfill AS's memory requirements under DOS, the various code generator modules of the DOS version were moved into an overlay which is part of the EXE file. A separate OVR file like in earlier versions of AS therefore dose not exist any more, AS will however still attempt to reduce the overlaying delays by using eventually available EMS or XMS memory. In case this results in trouble, you may suppress usage of EMS or XMS by setting the environment variable USEXMS or USEEMS to n. E.g., it is possible to suppress the using of XMS by the command:
SET USEXMS=nSince AS performs all in- and output via the operating system (and therefore it should run also on not 100% compatible DOS-PC's) and needs some basic display control, it emits ANSI control sequences during the assembly. In case you should see strange characters in the messages displayed by AS, your CONFIG.SYS is obviously lacking a line like this:
device=ansi.sysbut the further functions of AS will not be influenced hereby. Alternatively you are able to suppress the output of ANSI sequences completely by setting the environment variable USEANSI to n.
The DOS extender of the DPMI version can be influenced in its memory allocation strategies by a couple of environment variables; if you need to know their settings, you may look up them in the file DPMIUSER.DOC. ASX is additionally able to extend the available memory by a swap file. To do this, set up an environment variable ASXSWAP in the following way:
SET ASXSWAP=<size>[,file name]The size specification has to be done in megabytes and has to be done. The file name in contrast is optional; if it is missing, the file is named ASX.TMP and placed in the current directory. In any case, the swap file is deleted after program end.
The command line parameters can roughly be divided into three categories: switches, key file references (see below) and file specifications. Parameters of these two categories may be arbitrarily mixed in the command line. The assembler evaluates at first all parameters and then assembles the specified files. From this follow two things:
-queitinstead of
-quietAS will take the letters q, u, e, i, and t as individual switches. Multiple-letter switches additionally have the difference to single-letter switches that AS will accept an arbitrary mixture of upper and lower casing, whereas single-letter switches may have a different meaning depending on whether upper or lower case is used.
At the moment, the following switches are defined:
as test*.asm firstprog -cl /i c:\as\8051\includeAll files TEST*.ASM as well as the file FIRSTPROG.ASM will be assembled, whereby listings of all files are displayed on the console terminal. Additional sharefiles will be generated in the C- format. The assembler should search for additional include files in the directory C:\AS\8051\INCLUDE.
This example shows that the assembler assumes ASM as the default extension for source files.
A bit of caution should be applied when using switches that have optional arguments: if a file specification immediately follows such aswitch without the optional argument, AS will try to interprete the file specification as argument - what of course fails:
as -g test.asmThe solution in this case would either be to move the -g option the end or to specify an explicit MAP argument.
Beside from specifying options in the command line, permanently needed options may be placed in the environment variable ASCMD. For example, if someone always wants to have assembly listings and has a fixed directory for include files, he can save a lot of typing with the following command:
set ascmd=-L -i c:\as\8051\includeThe environment options are processed before the command line, so options in the command line can override contradicting ones in the environment variable.
In the case of very long path names, space in the ASCMD variable may become a problem. For such cases a key file may be the alternative, in which the options can be written in the same way as in the command line or the ASCMD-variable. But this file may contain several lines each with a maximum length of 255 characters. In a key file it is important, that for options which require an argument, switches and argument have to be written in the same line. AS gets informed of the name of the key file by a @ aheaded in the ASCMD variable, e.g.
set ASCMD=@c:\as\as.keyIn order to neutralize options in the ASCMD variable (or in the key file), prefix the option with a plus sign. For example, if you do not want to generate an assembly listing in an individual case, the option can be retracted in this way:
as +L <file>Naturally it is not consequently logical to deny an option by a plus sign.... UNIX soit qui mal y pense.
References to key files may not only come from the ASCMD variable, but also directly from the command line. Similarly to the ASCMD variable, prepend the file's name with a @ character:
as @<file> ....The options read from a key file in this situation are processed as if they had been written out in the command line in place of the reference, not like the key file referenced by the ASCMD variable that is processed prior to the command line options.
Referencing a key file from a key file itself is not allowed and will be answered wit an error message by AS.
In case that you like to start AS from another program or a shell and this shell hands over only lower-case or capital letters in the command line, the following workaround exists: if a tilde (~) is put in front of an option letter, the following letter is always interpreted as a lower-case letter. Similarly a # demands the interpretation as a capital letter. For example, the following transformations result for:
/~I ---> /i -#u ---> -UIn dependence of the assembly's outcome, the assembler ends with the following return codes:
511 KByte available memorydoes not indicate a shortly to come system crash due to memory lack, it simply shows the distance to the limit when OS/2 will push up the data segment's size again...
As there is no compatible way in C under different operating systens to find out the amount of available memory resp. stack, both lines are missing completely from the statistics the C version prints.
Like most assemblers, AS expects exactly one instruction per line (blank lines are naturally allowed as well). The lines must not be longer than 255 characters, additional characters are discarded.
A single line has following format:
[label[:]] <mnemonic>[.attr] [param[,param..]] [;comment]A line may also be split over several lines in the source file, continuation characters chain these parts together to a single line. One must however consider that, due to the internal buffer structure, the total line must not be longer than 256 characters. Line references in error messages always relate to the last line of such a composed source line.
The colon for the label is optional, in case the label starts in the first column (the consequence is that a machine or pseudo instruction must not start in column 1). It is necessary to set the colon in case the label does not start in the first column so that AS is able to distinguish it from a mnemonic. In the latter case, there must be at least one space between colon and mnemonic if the processor belongs to a family that supports an attribute that denotes an instruction format and is separated from the mnemonic by a colon. This restriction is necessary to avoid ambiguities: a distinction between a mnemonic with format and a label with mnemonic would otherwise be impossible.
Some signal processor families from Texas Instruments optionally use a double line (||) in place of the label to signify the prallel execution with the previous instruction(s). If these two assembler instructions become a single instruction word at machine level (C3x/C4x), an additional label in front of the second instruction of course does not make sense and is not allowed. The situation is different for the C6x with its instruction packets of variable length: If someone wants to jump into the middle of an instruction packet (bad style, if you ask me...), he has to place the necessary label before into a separate line. The same is valid for conditions, which however may be combined with the double line in a single source line.
The attribute is used by a couple of processors to specify variations or different codings of a certain instruction. The most prominent usage of the attibute is is the specification of the operand size, for example in the case of the 680x0 family (table 2.6).
attribute | arithmetic-logic instruction | jump instruction |
---|---|---|
B W L Q S D X P |
byte (8 bits) word (16 bits) long word (32 bits) quad word (64 bits) single precision (32 bits) double precision (64 bits) extended precision (80/96 bits) decimal floating point (80/96 bits) |
--------- --------- 16-bit-displacement --------- 8-bit-displacement --------- 32-bit-displacement --------- |
Since this manual is not also meant as a user's manual for the processor families supported by AS, this is unfortunately not the place to enumerate all possible attributes for all families. It should however be mentioned that in general, not all instructions of a given instruction set allow all attributes and that the omission of an attribute generally leads to the usage of the ''natural'' operand size of a processor family. For more thorough studies, consult a reasonable programmer's manual, e.g. [1] for the 68K's.
In the case of TLCS-9000, H8/500, and M16(C), the attribute serves both as an operand size specifier (if it is not obvious from the operands) and as a description of the instruction format to be used. A colon has to be used to separate the format from the operand size, e.g. like this:
add.w:g rw10,rw8This example does not show that there may be a format specification without an operand size. In contrast, if an operand size is used without a format specification, AS will automatically use the shortest possible format. The allowed formats and operand sizes again depend on the machine instruction and may be looked up e.g. in [132], [32], [55], resp. [56].
The number of instruction parameters depends on the mnemonic and is principally located between 0 and 20. The separation of the parameters from each other is to be performed only by commas (exception: DSP56xxx, its parallel data transfers are separated with blanks). Commas that are included in brackets or quotes, of course, are not taken into consideration.
Instead of a comment at the end, the whole line can consist of comment if it starts in the first column with a semicolon.
To separate the individual components you may also use tabulators instead of spaces.
The listing produced by AS using the command line options i or I is roughly divisible into the following parts :
In the first part, AS lists the complete contents of all source files including the produced code. A line of this listing has the following form:
[<n>] <line>/<address> <code> <source>In the field n, AS displays the include nesting level. The main file (the file where assembly was started) has the depth 0, an included file from there has depth 1 etc.. Depth 0 is not displayed.
In the field line, the source line number of the referenced file is issued. The first line of a file has the number 1. The address at which the code generated from this line is written follows after the slash in the field address.
The code produced is written behind address in the field code, in hexadecimal notation. Depending on the processor type and actual segment the values are formatted either as bytes or 16/32-bit-words. If more code is generated than the field can take, additional lines will be generated, in which case only this field is used.
Finally, in the field source, the line of the source file is issued in its original form.
The symbol table was designed in a way that it can be displayed on an 80-column display whenever possible. For symbols of ''normal length'', a double column output is used. If symbols exceed (with their name and value) the limit of 40 columns (characters), they will be issued in a separate line. The output is done in alphabetical order. Symbols that have been defined but were never used are marked with a star (*) as prefix.
The parts mentioned so far as well as the list of all macros/functions defined can be selectively masked out from the listing. This can be done by the already mentioned command line switch -t. There is an internal byte inside AS whose bits represent which parts are to be written. The assignment of bits to parts of the listing is listed in table 2.7.
bit | part |
---|---|
0 1 2 3 4 5 7 |
source file(s) + produced code symbol table macro list function list line numbering register symbol list character set table |
All bits are set to 1 by default, when using the switch
-t <mask>Bits set in <mask> are cleared, so that the respective listing parts are suppressed. Accordingly it is possible to switch on single parts again with a plus sign, in case you had switched off too much with the ASCMD variable... If someone wants to have, for example, only the symbol table, it is enough to write:
-t 2The usage list issues the occupied areas hexadecimally for every single segment. If the area has only one address, only this is written, otherwise the first and last address.
The cross reference list issues any defined symbol in alphabetical order and has the following form:
symbol <symbol name> (=<value>,<file>/<line>): file <file 1>: <n1>[(m1)] ..... <nk>[(mk)] . . file <file l>: <n1>[(m1)] ..... <nk>[(mk)]The cross reference list lists for every symbol in which files and lines it has been used. If a symbol was used several times in the same line, this would be indicated by a number in brackets behind the line number. If a symbol was never used, it would not appear in the list; The same is true for a file that does not contain any references for the symbol in question.
CAUTION! AS can only print the listing correctly if it was previously informed about the output media's page length and width! This has to be done with the PAGE instruction (see there). The preset default is a length of 60 lines and an unlimited line width.
Symbols are allowed to be up to 255 characters long (as hinted already in the introduction) and are being distinguished on the whole length, but the symbol names have to meet some conventions:
Symbol names are allowed to consist of a random combination of letters, digits, underlines and dots, whereby the first character must not be a digit. The dot is only allowed to meet the MCS-51 notation of register bits and should - as far as possible - not be used in own symbol names. To separate symbol names in any case the underline (_) and not the dot (.) should be used .
AS is by default not case-sensitive, i.e. it does not matter whether one uses upper or lower case characters. The command line switch U however allows to switch AS into a mode where upper and lower case makes a difference. The predefined symbol CASESENSITIVE signifies whether AS has been switched to this mode: TRUE means case-sensitiveness, and FALSE its absence.
Table 2.8 shows the most important symbols which are predefined by AS.
name | meaning |
---|---|
TRUE FALSE CONSTPI VERSION ARCHITECTURE DATE TIME MOMCPU MOMFILE MOMLINE MOMPASS MOMSECTION *, $ resp. PC |
logically ''true'' logically ''false'' Pi (3.1415.....) version of AS in BCD-coding, e.g. 1331 hex for version 1.33p1 target platform AS was compiled for, in the style processor-manufacturer-operating system date and time of the assembly (start) current target CPU (see the CPU instruction) current source file line number in source file number of the currently running pass name of the current section or an empty string current value of program counter |
CAUTION! While it does not matter in case-sensitive mode which combination of upper and lower case to use to reference predefined symbols, one has to use exactly the version given above (only upper case) when AS is in case-sensitive mode!
Additionally some pseudo instructions define symbols that reflect the value that has been set with these instructions. Their descriptions are explained at the individual commands belonging to them.
A hidden feature (that has to be used with care) is that symbol names may be assembled from the contents of string symbols. This can be achieved by framing the string symbol's name with braces and inserting it into the new symbol's name. This allows for example to define a symbol's name based on the value of another symbol:
cnt set cnt+1 temp equ "\{CNT}" jnz skip{temp} . . skip{temp}: nopCAUTION: The programmer has to assure that only valid symbol names are generated!
A complete list of all symbols predefined by AS can be found in appendix E.
Apart from its value, every symbol also owns a marker which signifies to which segment it belongs. Such a distinction is mainly needed for processors that have more than one address space. The additional information allows AS to issue a warning when a wrong instruction is used to access a symbol from a certain address space. A segment attribute is automatically added to a symbol when is gets defined via a label or a special instruction like BIT; a symbol defined via the ''allround instructions'' SET resp. EQU is however ''typeless'', i.e. its usage will never trigger warnings. A symbol's segment attribute may be queried via the buit-in function SYMTYPE, e.g.:
Label: . . Attr equ symtype(Label) ; results in 1The individual segment types have the assigned numbers listed in table 2.9. Register symbols which do not really fit into the order of normal symbols are explained in section 2.12. The SYMTYPE function delivers -1 as result when called with an undefined symbol as argument.
segment | return value |
<none> CODE DATA IDATA XDATA YDATA BITDATA IO REG ROMDATA <register symbol> |
0 1 2 3 4 5 6 7 8 9 128 |
Especially when dealing with programs that contain sequences of loops of if-like statements, one is continuously faced with the problem of inventing new names for labels - labels of which you know exactly that you will never need to reference them again afterwards and you really would like to get 'rid' of them somehow. A simple solution if you don't want to swing the large hammer of sections (see chapter 3.8) are temporary symbols which remain valid as long as a new, non-temporary symbol gets defined. Other assemblers offer a similar mechanism which is commonly referred as 'local symbols'; however, for the sake of a better distinction, I want to stay with the term 'temporary symbols'. AS knows three different types of temporary symbols, in the hope to offer everyone 'switching' to AS a solution that makes conversion as easy as possible. However, practically every assembler has its own interpretation of this feature, so there will be only few cases where a 1:1 solution for existing code:
A symbol whose name starts with two dollar signs (something that is neither allowed for non-temporary symbols nor for constants) is a named temporary symbol. AS keeps an internal counter which is reset to 0 before assembly begins and which gets incremented upon every definition of a non-temporary symbol. When a temporary symbol is defined or referenced, both leading dollar signs are discarded and the counter's current value is appended. This way, one regains the used symbol names with every definition of a non-temporary symbol - but you also cannot reach the previously symbols any more! Temporary symbols are therefore especially suited for usage in small instruction blocks, typically a dozen of machine instructions, definitely not more than one screen. Otherwise, one easily gets confused...
Here is a small example:
$$loop: nop dbra d0,$$loop split: $$loop: nop dbra d0,$$loopWithout the non-temporary label between the loops, of course an error message about a double-defined symbol would be the result.
For all those who regard named temporary symbols still as too complicated, there is an even simpler variant: If one places a single puls or minus sign as a label, this is converted to symbol names of __forwnn respectively __backmm, with nn respectively mm being counters that start counting at zero. Those symbols are referenced via the special names - -- --- respectively + ++ +++, which refer to the three last 'minus symbols' and the next three 'plus symbols'. Therefore, the selection between these two variants depends on whether one wants to forward- or backward-reference a symbol.
Apart from plus and minus, defining nameless temporary symbols also exists in a third variant, namely a slash (/). A temporary symbol defined in this way may be referenced both backward and forward, i.e. it is treated either as a plus or a minus, depending on the way it is being referenced.
Nameless temporary symbols are usually used in constructs that fit on one screen page, like skipping a few machine instructions or tight loops - things would becone to puzzling otherwise (this only a good advice, however...). An example for this is the following piece of code, this time as 65xx code:
cpu 6502 - ldx #00 - dex bne - ; branch to 'dex' lda RealSymbol beq + ; branch to 'bne --' jsr SomeRtn iny + bne -- ; branch to 'ldx #00' SomeRtn: rts RealSymbol: dfs 1 inc ptr bne + ; branch to 'tax' inc ptr+1 + tax bpl ++ ; branch to 'dex' beq + ; branch forward to 'rts' lda #0 / rts ; slash used as wildcard. + dex beq - ; branch backward to 'rts' ptr: dfs 2
This is maybe the type of temporary symbols that is nearest to the concept of local symbols and sections. Whenever a symbol's name begins with a dot (.), the symbol is not directly stored with this name in the symbol table. Instead, the name of the most recently-defined symbol not beginning with a dot is prepended to the symbols name. This way, 'non-dotted' symbols take the role of section separators and 'dotted' symbol names may be reused after a 'non-dotted' symbol has been defined. Take a look at the following little example:
proc1: ; non-temporary symbol 'proc1' .loop moveq #20,d0 ; actually defines 'proc1.loop' dbra d0,.loop rts proc2: ; non-temporary symbol 'proc2' .loop moveq #10,d1 ; actually defines 'proc2.loop' jsr proc1 dbra d1,.loop rtsNote that it is still possible to access all temporary symbols, even without being in the same 'area', by simply using the composed name (like 'proc2.loop' in the previous example).
It is principally possible to combine composed temporary symbols with sections, which makes them also to local symbols. Take however into account that the most recent non-temporary symbol is not stored per-section, but simply globally. This may change however in a future version, so one shouldn't rely on the current behaviour.
In most places where the assembler expects numeric inputs, it is possible to specify not only simple symbols or constants, but also complete formula expressions. The components of these formula expressions can be either single symbols and constants. Constants may be either integer, floating point, or string constants.
Integer constants describe non-fractional numbers. They may either be written as a sequence of digits or as a sequence of characters enclosed in single quotation marks. In case they are written as a sequence of digits, this may be done in different numbering systems (table 2.10).
Intel mode (Intel, Zilog, Thomson Texas, Toshiba, NEC, Siemens, Philips, Fujitsu, Fairchild, Intersil) |
Motorola mode (Rockwell, Motorola, Microchip, Thomson, Hitachi) |
C mode (PowerPC, AMD 29K, National, Symbios, Atmel) |
|
---|---|---|---|
decimal hexadecimal binary octal |
direct followed by H followed by B followed by O followed by Q |
direct prepended $ prepended % prepended @ |
direct prepended 0x prepended 0b prepended 0 |
In case the numbering system has not been explicitly stated by adding the special control characters listed in the table, AS assumes the base given with the RADIX statement (which has itself 10 as default). This statement allows to set up 'unusual' numbering systems, i.e. others than 2, 8, 10, or 16.
Valid digits are numbers from 0 to 9 and letters from A to Z (value 10 to 35) up to the numbering system's base minus one. The usage of letters in integer constants however brings along some ambiguities since symbol names also are sequences of numbers and letters: a symbol name must not start with a character from 0 to 9. This means that an integer constant which is not clearly marked a such with a special prefix character never may begin with a letter. One has to add an additional, otherwise superfluous zero in front in such cases. The most prominent case is the writing of hexadecimal constants in Intel mode: If the leftmost digit is between A and F, the trailing H doesn't help anything, an additional 0 has to be prefixed (e.g. 0F0H instead of F0H). The Motorola and C syntaxes whis both mark the numbering system at the front of a constant do not have this problem (hehehe..).
Quite tricky is furthermore that the higher the default numbering system set via RADIX becomes, the more letters used to denote numbering systems in Intel and C syntax become 'eaten'. For example, you cannot write binary constants anymore after a RADIX 16, and starting at RADIX 18, the Intel syntax even doesn't allow to write hexadecimal constants any more. Therefore CAUTION!
With the help of the RELAXED instruction (see section 3.9.6), the strict assignment of a syntax to a certain target processor can be removed. The result is that an arbitrary syntax may be used (loosing compatibility to standard assemblers). This option is however turned off by default. This option also opens the opportunity for a fourth way of writing integer constants, a way that is sometimes found on other assemblers: This way puts the actual value into apostrophes and prepends the numbering system ('x' or 'h' for hexadecimal, 'o' for octal and 'b' for binary). So, the integer constant 305419896 can be written in the following ways:
x'12345678' h'12345678' o'2215053170' b'00010010001101000101011001111000'This syntax is noth the default for any processor architecture and only available in RELAXED mode. Its main purpose is the easier porting of existing sources and is not recommended for new programs.
Integer constants may also be written as ASCII values, like in the following examples:
'A' ==$41 'AB' ==$4142 'ABCD' ==$41424344It is important to write the characters in single quotes, to distinguish them from string constants (discussed somewhat later).
Floating point constants are to be written in the usual scientific notation, which is known in the most general form:
[-]<integer digits>[.post decimal positions][E[-]exponent]CAUTION! The assembler first tries to interprete a constant as an integer constant and makes a floating-point format try only in case the first one failed. If someone wants to enforce the evaluation as a floating point number, this can be done by dummy post decimal positions, e.g. 2.0 instead of 2.
String constants have to be included in double quotation marks (to distinguish them from the abovementioned ASCII-integers). In order to make it possible to write quotation marks and special characters without trouble in string constants, an ''escape mechanism'' has been implemented, which should sound familiar for C programmers:
The assembler understands a backslash (\) with a following decimal number of three digits maximum in the string as a character with the according decimal ASCII value. The numerical value may alternitavely be written in hexadecimal or octal notation if it is prefixed with an x resp. a 0. In case of hexadecimal notation, the maximum number of digits is limited to 2. For example, it is possible to include an ETC character by writing\3. But be careful with the definition of NUL characters! The C version currently uses C strings to store strings internally. As C strings use a NUL character for termination, the usage of NUL characters in strings is currently not portable!
Some frequently used control characters can also be reached with the following abbreviations:
\b : Backspace \a : Bell \e : Escape \t : Tabulator \n : Linefeed \r : Carriage Return \\ : Backslash \' or \H : Apostrophe \" or \I : Quotation marksBoth upper and lower case characters may be used for the identification letters.
By means of this escape character, you can even work formula expressions into a string, if they are enclosed by braces: e.g.
message "root of 81 : \{sqrt(81)}"results in
root of 81 : 9AS chooses with the help of the formula result type the correct output format, further string constants, however, are to be avoided in the expression. Otherwise the assembler will get mixed up at the transformation of capitals into lower case letters. Integer results will by default be written in hexadecimal notation, which may be changed via the OUTRADIX instruction.
Except for the insertion of formula expressions, you can use this ''escape-mechanism'' as well in ASCII defined integer constants, like this:
move.b #'\n',d0However, everything has its limits, because the parser with higher priority, which disassembles a line into op-code and parameters, does not know what it is actually working with, e.g. here:
move.l #'\'abc',d0After the third apostrophe, it will not find the comma any more, because it presumes that it is the start of a further character constant. An error message about a wrong parameter number is the result. A workaround would be to write e.g., \i instead of \'.
The calculation of intermediary results within formula expressions is always done with the highest available resolution, i.e. 32 bits for integer numbers, 80 bit for floating point numbers and 255 characters for strings. An possible test of value range overflows is done only on the final result.
The portable C version only supports floating point values up to 64 bits (resulting in a maximum value of roughly 10 308), but in turn features integer lengths of 64 bits on some platforms.
The assembler provides the operands listed in table 2.11 for combination.
operand | function | #operands | integer | float | string | rank |
---|---|---|---|---|---|---|
<> >= <= < > = == !! || && ~~ - + # / * ^ ! | & >< >> << ~ |
inequality greater or equal less or equal truly smaller truly greater equality alias for = log. XOR log. OR log. AND log. NOT difference sum modulo division quotient product power binary XOR binary OR binary AND mirror of bits log. shift right log. shift left binary NOT |
2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 1 |
yes yes yes yes yes yes yes yes yes yes yes yes yes yes*) yes yes yes yes yes yes yes yes yes |
yes yes yes yes yes yes no no no no yes yes no yes yes yes no no no no no no no |
yes yes yes yes yes yes no no no no no yes no no no no no no no no no no no |
14 14 14 14 14 14 13 12 11 2 10 10 9 9 9 8 7 6 5 4 3 3 1 |
*) remainder will be discarded |
''Rank'' is the priority of an operator at the separation of expressions into subexpressions. The operator with the highest rank will be evaluated at the very end. The order of evaluation can be defined by new bracketing.
The compare operators deliver TRUE in case the condition fits, and FALSE in case it doesn't. For the logical operators an expression is TRUE in case it is not 0, otherwise it is FALSE.
The mirroring of bits probably needs a little bit of explanation: the operator mirrors the lowest bits in the first operand and leaves the higher priority bits unchanged. The number of bits which is to be mirrored is given by the right operand and may be between 1 and 32 .
A small pitfall is hidden in the binary complement: As the computation is always done with 32 resp. 64 bits, its application on e.g. 8-bit masks usually results in values taht do not fit into 8-bit numbers any more due to the leading ones. A binary AND with a fitting mask is therefore unavoidable!
In addition to the operators, the assembler defines another line of primarily transcendental functions with floating point arguments which are listed in tables 2.12 and 2.13.
name | meaning | argument | result |
---|---|---|---|
SQRT SIN COS TAN COT ASIN ACOS ATAN ACOT EXP ALOG ALD SINH COSH TANH COTH LN LOG LD ASINH ACOSH ATANH ACOTH INT |
square root sine cosine tangent cotangent inverse sine inverse cosine inverse tangent inverse cotangent exponential function 10 power of argument 2 power of argument hyp. sine hyp. cosine hyp. tangent hyp. cotangent nat. logarithm dec. logarithm bin. logarithm inv. hyp. Sine inv. hyp. Cosine inv. hyp. Tangent inv. hyp. Cotangent integer part |
arg >= 0 arg in R arg in R arg <> (2n+1)*(Pi)/(2) arg <> n*Pi | arg | <= 1 | arg | <= 1 arg in R arg in R arg in R arg in R arg in R arg in R arg in R arg in R arg <> 0 arg > 0 arg > 0 arg > 0 arg in R arg >= 1 arg < 1 arg > 1 arg in R |
floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point floating point |
BITCNT FIRSTBIT |
number of one's lowest 1-bit |
integer integer |
integer integer |
name | meaning | argument | result |
---|---|---|---|
LASTBIT BITPOS SGN ABS TOUPPER TOLOWER UPSTRING LOWSTRING STRLEN SUBSTR CHARFROMSTR STRSTR VAL EXPRTYPE |
highest 1-bit unique 1-bit sign (0/1/-1) absolute value matching capital matching lower case changes all characters into capitals changes all characters into to lower case returns the length of a string extracts parts of a string extracts a character from a string searches a substring in a string evaluates contents as expression delivers type of argument |
integer integer floating point or integer integer or floating point integer integer string string string string, integer, integer string, integer string, string string integer, float, string |
integer integer integer integer or floating point integer integer string string integer string integer integer depends on argument 0 1 2 |
The functions FIRSTBIT, LASTBIT, and BITPOS return -1 as result if no resp. not exactly one bit is set. BITPOS additionally issues an error message in such a case.
The string function SUBSTR expects the source string as first parameter, the start position as second and the number of characters to be extracted as third parameter (a 0 means to extract all characters up to the end). Similarly, CHARFROMSTR expects the source string as first argument and the character position as second argument. In case the position argument is larger or equal to the source string's length, SUBSTR returns an empty string while CHARFROMSTR returns -1. A position argument smaller than zero is treated as zero by SUBSTR, while CHARFROMSTR will return -1 also in this case.
Here is an example how to use these both functions. The task is to put a string into memory, with the string end being signified by a set MSB in the last character:
dbstr macro arg if strlen(arg) > 1 db substr(arg, 0, strlen(arg) - 1) endif if strlen(arg) > 0 db charfromstr(arg, strlen(arg) - 1) | 80h endif endm
STRSTR returns the first occurence of the second string within the first one resp. -1 if the search pattern was not found. Similarly to SUBSTR and CHARFROMSTR, the first character has the position 0.
If a function expects floating point arguments, this does not mean it is impossible to write e.g.
sqr2 equ sqrt(2)In such cases an automatic type conversion is engaged. In the reverse case the INT-function has to be applied to convert a floating point number to an integer. When using this function, you have to pay attention that the result produced always is a signed integer and therefore has a value range of approximately +/-2.0E9.
When AS is switched to case-sensitive mode, predefined functions may be accessed with an arbitrary combination of upper and lower case (in contrast to predefined symbols). However, in the case of user-defined functions (see section 3.4.9), a distinction between upper and lower case is made. This has e.g. the result that if one defines a function Sin, one can afterwards access this function via Sin, but all other combinations of upper and lower case will lead to the predefined function.
For a correct conversion of lower case letters into capital letters a DOS version >= 3.30 is required.
This section is the result of a significant amount of hate on the (legal) way some people program. This way can lead to trouble in conjunction with AS in some cases. The section will deal with so-called 'forward references'. What makes a forward reference different from a usual reference? To understand the difference, take a look at the following programming example (please excuse my bias for the 68000 family that is also present in the rest of this manual):
move.l #10,d0 loop: move.l (a1),d1 beq skip neg.l d1 skip: move.l d1,(a1+) dbra d0,loopIf one overlooks the loop body with its branch statement, a program remains that is extremely simple to assemble: the only reference is the branch back to the body's beginning, and as an assembler processes a program from the beginning to the end, the symbol's value is already known before it is needed the first time. If one has a program that only contains such backward references, one has the nice situation that only one pass through the source code is needed to generate a correct and optimal machine code. Some high level languages like Pascal with their strict rule that everything has to be defined before it is used exploit exactly this property to speed up the compilation.
Unfortunately, things are not that simple in the case of assembler, because one sometimes has to jump forward in the code or there are reasons why one has to move variable definitions behind the code. For our example, this is the case for the conditional branch that is used to skip over another instruction. When the assembler hits the branch instruction in the first pass, it is confronted with the situation of either leaving blank all instruction fields related to the target address or offering a value that ''hurts noone'' via the formula parser (which has to evaluate the address argument). In case of a ''simple'' assembler that supports only one target architecture with a relatively small number of instructions to treat, one will surely prefer the first solution, but the effort for AS with its dozens of target architectures would have become extremely high. Only the second way was possible: If an unknown symbol is detected in the first pass, the formula parser delivers the program counter's current value as result! This is the only value suitable to offer an address to a branch instruction with unknown distance length that will not lead to errors. This answers also a frequently asked question why a first-pass listing (it will not be erased e.g. when AS does not start a second pass due to additional errors) partially shows wrong addresses in the generated binary code - they are the result of unresolved forward references.
The example listed above however uncovers an additional difficulty of forward references: Depending on the distance of branch instruction and target in the source code, the branch may be either long or short. The decision however about the code length - and therefore about the addresses of following labels - cannot be made in the first pass due to missing knowledge about the target address. In case the programmer did not explicitly mark whether a long or short branch shall be used, genuine 2-pass assemblers like older versions of MASM from Microsoft ''solve'' the problem by reserving space for the longest version in the first pass (all label addresses have to be fixed after the first pass) and filling the remaining space with NOPs in the second pass. AS versions up to 1.37 did the same before I switched to the multipass principle that removes the strict separation into two passes and allows an arbitrary number of passes. Said in detail, the optimal code for the assumed values is generated in the first pass. In case AS detects that values of symbols changed in the second pass due to changes in code lengths, simply a third pass is done, and as the second pass'es new symbol values might again shorten or lengthen the code, a further pass is not impossible. I have seen 8086 programs that needed 12 passes to get everything correct and optimal. Unfortunately, this mechanism does not allow to specify a maximum number passes; I can only advise that the number of passes goes down when one makes more use of explicit length specifications.
Especially for large programs, another situation might arise: the position of a forward directed branch has moved so much in the second pass relative to the first pass that the old label value still valid is out of the allowed branch distance. AS knows of such situations and suppresses all error messages about too long branches when it is clear that another pass is needed. This works for 99% of all cases, but there are also constructs where the first critical instruction appears so early that AS had no chance up to now to recognize that another pass is needed. The following example constructs such a situation with the help of a forward reference (and was the reason for this section's heading...):
cpu 6811 org $8000 beq skip rept 60 ldd Var endm skip: nop Var equ $10Due to the address position, AS assumes long addresses in the first pass for the LDD instructions, what results in a code length of 180 bytes and an out of branch error message in the second pass (at the point of the BEQ instruction, the old value of skip is still valid, i.e. AS does not know at this point that the code is only 120 bytes long in reality) is the result. The error can be avoided in three different ways:
move.l #sym2,d0 sym2 equ sym1+5 sym1 equ 0one gets an error message due to an undefined symbol in the second pass...but why on earth do people do such things?
Admittedly, this was quite a lengthy excursion, but I thought it was necessary. Which is the essence you should learn from this section?
valid for: PowerPC, M-Core, 4004/4040, MCS-48/(2)51, 80C16x, AVR, XS1, Z8
Sometimes it is desirable not only to assign symbolic names to memory addresses or constants, but also to a register, to emphasize its function in a certain program section. This is no problem for processors that treat registers simply as another address space, as this allows to use numeric expressions and one can use simple EQUs to define such symbols. (e.g. for the MCS-96 or TMS70000). However, for most processors, register identifiers are fixed literals which are seperately treated by AS for speed reasons. A special mechanism is therefore necessary to define symbolic register names. A register symbol is usually defined via the REG instruction, which has otherwise the same syntax as an EQU definition. This however has a couple of restrictions: A register symbol is a pure character string stored 'as is' which may exclusively be used this way. For example, no arithmetic is allowed to calculate a register's successor, like in the following example:
myreg reg r17 ; definition of register symbol addi myreg+1,3 ; does not work!Additionally, a register symbol has to be defined prior to ist first usage; a forward reference would have the result that AS suspects a forward reference to a memory location in case a register symbol is not found. Since the usage of memory operands is far more limited on most processors, a bunch of errors would be the result...
Analogous to ordinary symbols, register symbols are local to sections and it is possible to access a register symbol from a specific section by appending the section's name enclosed in brackets. Due to the missing ability to do forward references, there is nothing like a FORWARD directive, and an export by something comparable to PUBLIC or GLOBAL is also not possible since register symbols generally have their meaning in a small context.
If there is both an ordinary and a register symbol of same name present in a context, the register symbol will be preferred. This is however not the case when the name is embedded into a complex expression (parentheses are sufficient!), the normal symbol will be used then.
This function is a by-product from the old pure-68000 predecessors of AS, I have kept them in case someone really needs it. The basic problem is to access certain symbols produced during assembly, because possibly someone would like to access the memory of the target system via this address information. The assembler allows to export symbol values by means of SHARED pseudo commands (see there). For this purpose, the assembler produces a text file with the required symbols and its values in the second pass. This file may be included into a higher-level language or another assembler program. The format of the text file (C, Pascal or Assembler) can be set by the command line switches p, c or, a.
CAUTION! If none of the switches is given, no file will be generated and it makes no difference if SHARED-commands are in the source text or not!
When creating a Sharefile, AS does not check if a file with the same name already exists, such a file will be simply overwritten. In my opinion a request does not make sense, because AS would ask at each run if it should overwrite the old version of the Sharefile, and that would be really annoying...
Common microcontroller families are like rabbits: They become more at a higher speed than you can provide support for them. Especially the development of processor cores as building blocks for ASICs and of microcontroller families with user-definable peripherals has led to a steeply rising number of controllers that only deviate from a well-known type by a slightly modified peripheral set. But the distinction among them is still important, e.g. for the design of include files that only define the appropriate subset of peripherals. I have struggled up to now to integrate the most important reperesentatives of a processor family into AS (and I will continue to do this), but sometimes I just cannot keep pace with the development...there was an urgent need for a mechanism to extend the list of processors by the user.
The result are processor aliases: the alias command line option allows to define a new processor type, whose instruction set is equal to another processor built into AS. After switching to this processor via the CPU instruction, AS behaves exactly as if the original processor had been used, with a single difference: the variables MOMCPU resp. MOMCPUNAME are set to the alias name, which allows to use the new name for differentiation, e.g. in include files.
There were two reasons to realize the definition of aliases by the command line and not by pseudo instructions: first, it would anyway be difficult to put the alias definitions together with register definitions into a single include file, because a program that wants to use such a file would have to include it before and after the CPU instruction - an imagination that lies somewhere between inelegant and impossible. Second, the definition in the command line allows to put the definitions in a key file that is executed automatically at startup via the ASCMD variable, without a need for the program to take any further care about this.
Not all pseudo instructions are defined for all processors. A note that shows the range of validity is therefore prepended to every individual description.
valid for: all processors, CONSTANT only for KCPSM(3)
SET and EQU allow the definition of typeless constants, i.e. they will not be assigned to a segment and their usage will not generate warnings because of segment mixing. EQU defines constants which can not be modified (by EQU) again, but SET permits the definition of variables, which can be modified during the assembly. This is useful e.g. for the allocation of resources like interrupt vectors, as shown in the following example:
VecCnt set 0 ; somewhere at the beginning . . . DefVec macro Name ; allocate a new vector Name equ VecCnt VecCnt set VecCnt+4 endm . . . DefVec Vec1 ; results in Vec1=0 DefVec Vec2 ; results in Vec2=4constants and variables are internally stored in the same way, the only difference is that they are marked as unchangeable if defined via EQU. Trying to change a constant with SET will result in an error message.
EQU/SET allow to define constants of all possible types, e.g.
IntTwo equ 2 FloatTwo equ 2.0Some processors unfortunately have already a SET instruction. For these targets, EVAL must be used instead of SET if no differentiation via the argument count is possible.
A simple equation sign may be used instead of EQU. Similarly, one may simply write := instead of SET resp. EVAL. Furthermore, there is an 'alternate' syntax that does not take the symbol's name from the label field, but instead from the first argument. So for instance, it is valid to write:
EQU IntTwo,2 EQU FloatTwo,2.0
For compatibility reasons to the original assembler, the KCPSM target also knows the CONSTANT statement, which - in contrast to EQU - takes both name and value as argument. For example:
CONSTANT const1, 2CONSTANT is however limited to integer constants.
Symbols defined with SET or EQU are typeless by default, but optionally a segment name (CODE, DATA, IDATA, XDATA, YDATA, BITDATA, IO, or REG) or MOMSEGMENT for the currently active segment may be given as a second or thirsd parameter, allowing to assign the symbol to a specific address space. AS does not check at this point if the used address space exists on the currently active target processor!
valid for: various, SFRB only MCS-51
These instructions act like EQU, but symbols defined with them are assigned to the directly addressable data segment, i.e. they serve preferential for the definition of RAM-cells and (as the name lets guess) hardware registers mapped into the data area. The allowed range of values is equal to the range allowed for ORG in the data segment (see section 3.2.1). The difference between SFR and SFRB is that SFRB marks the register as bit addressable, which is why AS generates 8 additional symbols which will be assigned to the bit segment and carry the names xx.0 to xx.7, e.g.
PSW sfr 0d0h ; results in PSW = D0H (data segment) PSW sfrb 0d0h ; results in extra PSW.0 = D0H (bit) ; to PSW.7 = D7H (bit)The SFRB instruction is not any more defined for the 80C251 as it allows direct bit access to all SFRs without special bit symbols; bits like PSW.0 to PSW.7 are automatically present.
Whenever a bit-addressable register is defined via SFRB, AS checks if the memory address is bit addressable (range 20h..3fh resp. 80h, 88h, 90h, 98h...0f8h). If it is not bit-addressable, a warning is issued and the generated bit symbols are undefined.
valid for: DSP56xxx
Also the DSP56000 has a few peripheral registers memory-mapped to the RAM, but the affair becomes complicated because there are two data areas, the X- and Y-area. This architecture allows on the one hand a higher parallelism, but forces on the other hand to divide the normal SFR instruction into the two above mentioned variations. They works identically to SFR, just that XSFR defines a symbol in the X- addressing space and YSFR a corresponding one in the Y-addressing space. The allowed value range is 0..$ffff.
valid for: all processors
The function of the LABEL instruction is identical to EQU, but the symbol does not become typeless, it gets the attribute ''code''. LABEL is needed exactly for one purpose: Labels are normally local in macros, that means they are not accessible outside of a macro. With an EQU instruction you could get out of it nicely, but the phrasing
<name> label $generates a symbol with correct attributes.
valid for: MCS/(2)51, XA, 80C166, 75K0, ST9, AVR, S12Z, SX20/28, H16
BIT serves to equate a single bit of a memory cell with a symbolic name. This instruction varies from target platform to target platform due to the different ways in which processors handle bit manipulation and addressing:
The MCS/51 family has an own address space for bit operands. The function of BIT is therefore quite similar to SFR, i.e. a simple integer symbol with the specified value is generated and assigned to the BDATA segment. For all other processors, bit addressing is done in a two-dimensional fashion with address and bit position. In these cases, AS packs both parts into an integer symbol in a way that depends on the currently active target processor and separates both parts again when the symbol is used. The latter is is also valid for the 80C251: While an instruction like
My_Carry bit PSW.7would assign the value 0d7h to My_Carry on an 8051, a value of 070000d0h would be generated on an 80C251, i.e. the address is located in bits 0..7 and the bit position in bits 24..26. This procedure is equal to the way the DBIT instruction handles things on a TMS370 and is also used on the 80C166, with the only difference that bit positions may range from 0..15:
MSB BIT r5.15On a Philips XA, the bit's address is located in bits 0..9 just with the same coding as used in machine instructions, and the 64K bank of bits in RAM memory is placed in bits 16..23.
The BIT instruction of the 75K0 family even goes further: As bit expressions may not only use absolute base addresses, even expressions like
bit1 BIT @h+5.2are allowed.
The ST9 in turn allows to invert bits, what is also allowed in the BIT instruction:
invbit BIT r6.!3More about the ST9's BIT instruction can be found in the processor specific hints.
In case of H16, note that the address and bit position arguments are swapped. This was done to make the syntax of BIT consistent with the machine instructions that maipulate individual bits.
valid for: TMS 370xxx
Though the TMS370 series does not have an explicit bit segment, single bit symbols may be simulated with this instruction. DBIT requires two operands, the address of the memory cell that contains the bit and the exact position of the bit in the byte. For example,
INT3 EQU P019 INT3_ENABLE DBIT 0,INT3defines the bit that enables interrupts via the INT3 pin. Bits defined this way may be used in the instructions SBIT0, SBIT1, CMPBIT, JBIT0, and JBIT.
valid for: S12Z
The S12Z family's processor core provides instructions to manipulate individual bits in registers or memory cells. To conveniently address bits in the CPU's I/O area (first 4 Kbytes of the address space), a bit may be given a symbolic name. The bit is defined by its memory address and the bit position:
<name> defbit[.size] <address>,<position>The address must be located within the first 4 Kbytes, and the operand size may be 8, 16, or 32 bits (size=b/w/l). Consequently, the position may at most be 7, 15 or 31. If no operand size is given, byte size (.b) is assumed. A bit defined this way may be used as argument for the instructions BCLR, BSET, BTGL, BRSET, and BRCLR:
mybit defbit.b $200,4 bclr.b $200,#4 bclr mybitBoth uses of bclr in this example generate identical code. Since a bit defined this way ''knows'' its size, the size attribute may be omitted when using it.
It is also possible to define bits that are located within a structure's element:
mystruct struct dots reg ds.w 1 flag defbit reg,4 ends org $100 data mystruct bset data.flag ; same as bset.w $100,#4
valid for: S12Z
The S12Z family's CPU core not only deals with individual bits, it is also able to extract a field of consecutive bits from an 8/16/24/32 value or to insert a bit field into such a value. Similar to DEFBIT, a bit field may be defined symbolically:
<Name> defbitfield[.size] <address>,<width>:<position>Opposed to individual bits, an operand size of 24 bits (.p) is also alloweed. The range of position and width is accordingly 0 to 23 resp. 1 to 24. It is also allowed to define bit fields as parts of structures:
mystruct struct dots reg ds.w 1 clksel defbitfield reg,4:8 ends org $100 data mystruct bfext d2,data.clksel ; fetch $100.w bits 4..11 ; to D2 bits 0..7 bfins data.clksel,d2 ; insert D2 bits 0..7 into ; $100.w bits 4..11The internal representation of bits defined via DEFBIT is equivalent to bit fields with a width of one. Therefore, a symbolically defined bit may also be used as argument for BFINS and BFEXT.
valid for: 8080/8085/8086, XA, Z80, 320C2x/5x, TLCS-47, AVR, F8
PORT works similar to EQU, just the symbol becomes assigned to the I/O-address range. Allowed values are 0..7 for the 3201x, 0..15 for the 320C2x, 0..65535 for the 8086 and 320C5x, 0..63 for the AVR, and 0..255 for the rest.
Example : an 8255 PIO is located at address 20H:
PIO_port_A port 20h PIO_port_B port PIO_port_A+1 PIO_port_C port PIO_port_A+2 PIO_ctrl port PIO_port_A+3
valid for: AVR, M*Core, ST9, 80C16x, KCPSM ( NAMEREG valid only for KCPSM(3)), LatticeMico8, MSP430(X)
Though it always has the same syntax, this instruction has a slightly different meaning from processor to processor: If the processor uses a separate addressing space for registers, REG has the same effect as a simple EQU for this address space (e.g. for the ST9). REG defines register symbols for all other processors whose function is described in section 2.12.
NAMEREG exists for compatibility reasons to the original KCPSM assembler. It has an identical function, however both register and symbolic name are given as arguments, for example:
NAMEREG s08, treg
valid for: 8X30x
LIV and RIV allow to define so-called ''IV bus objects''. These are groups of bits located in a peripheral memory cell with a length of 1 up to 8 bits, which can afterwards be referenced symbolically. The result is that one does not anymore have to specify address, position, and length separately for instructions that can refer to peripheral bit groups. As the 8X30x processors feature two peripheral address spaces (a ''left'' and a ''right'' one), there are two separate pseudo instructions. The parameters of these instructions are however equal: three parameters have to be given that specify address, start position and length. Further hints for the usage of bus objects can be found in section 4.22 .
valid for: all processors
Single board systems, especially when driving LCDs, frequently use character sets different to ASCII. So it is probably purely coincidental that the umlaut coding corresponds with the one used by the PC. To avoid error-prone manual encoding, the assembler contains a translation table for characters which assigns a target character to each source-code. To modify this table (which initial translates 1:1), one has to use the CHARSET instruction. CHARSET may be used with different numbers and types of parameters. If there is only a single parameter, it has to be a string expression which is interpreted as a file name by AS. AS reads the first 256 bytes from this table and copies them into the translation table. This allows to activate complex, externally generated tables with a single statement. For all other variants, the first parameter has to be an integer in the range of 0 to 255 which designates the start index of the entries to be modified in the translation table. One or two parameters follow, giving the type of modification:
A single additional integer modies exactly one entry. For example,
CHARSET 'ä',128means that the target system codes the 'ä' into the number 128 (80H). If however two more integers are given, the first one describes the last entry to be modified, and the second the new value of the first table entry. All entries up to the index end are loaded sequentially. For example, in case that the target system does not support lower-case characters, a simple
CHARSET 'a','z','A'translates all lower-case characters automatically into the matching capital letters.
For the last variant, a string follows the start index and contains the characters to be placed in the table. The last example therefore may also be written as
CHARSET 'a',"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
CHARSET may also be called without any parameters, which however has a drastical effect: the translation table is reinitialized to its initial state, i.e. all character translations are removed.
CAUTION! CHARSET not only affects string constants stored in memory, but also integer constants written as ''ASCII''. This means that an already modified translation table can lead to other results in the above mentioned examples!
valid for: all processors
Though the CHARSET statement gives unlimited freedom in the character assignment between host and target platform, switching among different character sets can become quite tedious if several character sets have to be supported on the target platform. The CODEPAGE instruction however allows to define and keep different character sets and to switch with a single statement among them. CODEPAGE expects one or two arguments: the name of the set to be used hereafter and optionally the name of another table that defines its initial contents (the second parameter therefore only has a meaning for the first switch to the table when AS automatically creates it). If the second parameter is missing, the initial contents of the new table are copied from the previously active set. All subsequent CHARSET statements only modify the new set.
At the beginning of a pass, AS automatically creates a single character set with the name STANDARD with a one-to-one translation. If no CODEPAGE instructions are used, all settings made via CHARSET refer to this table.
valid for: all processors
Similar to the same-named instruction known from C, ENUM is used to define enumeration types, i.e. a sequence of integer constants that are assigned sequential values starting at 0. The parameters are the names of the symbols, like in the following example:
ENUM SymA,SymB,SymCThis instruction will assign the values 0, 1, and 2 to the symbols SymA, SymB, and SymC.
If you want to split an enumeration over more than one line, use NEXTENUM instead of ENUM for the second and all following lines. The internal counter that assigns sequential values to alls symbols will then not be reset to zero, like in the following case:
ENUM January=1,February,March,April,May,June NEXTENUM July,August,September,October NEXTENUM November,DecemberThis example also demonstrates that it is possible to assign explicit values to individual symbols. The internal counter will be updated accordingly if this feature is used.
A definition of a symbol with ENUM is equal to a definition with EQU, i.e. it is not possible to assign a new value to a symbol that already exists.
The ENUMCONF statement allows to influence the behaviour of ENUM. ENUMCONF accepts one or two arguments. The first argument is always the value the internal counter is incremented for every symbol in an enumeration. For instance, the statement
ENUMCONF 2has the effect that symbols get the values 0,2,4,6... instead of 0,1,2,3...
The second (optional) argument of ENUMCONF rules which address space the defined symbols are assigned to. By default, symbols defined by ENUM are typeless. For instance, the statement
ENUMCONF 1,CODEdefines that they should be assigned to the instruction address space. The names of the address spaces are the same as for the SEGMENT instruction (3.2.13), with the addition of NOTHING to generate typeless symbols again.
valid for: all processors
PUSHV and POPV allow to temporarily save the value of a symbol (that is not macro-local) and to restore it at a later point of time. The storage is done on stacks, i.e. Last-In-First-Out memory structures. A stack has a name that has to fulfill the general rules for symbol names and it exists as long as it contains at least one element: a stack that did not exist before is automatically created upon PUSHV, and a stack becoming empty upon a POPV is deleted automatically. The name of the stack that shall be used to save or restore symbols is the first parameter of PUSH resp. POPV, followed by a list of symbols as further parameters. All symbols referenced in the list already have to exist, it is therefore not possible to implicitly define symbols with a POPV instruction.
Stacks are a global resource, i.e. their names are not local to sections.
It is important to note that symbol lists are always processed from left to right. Someone who wants to pop several variables from a stack with a POPV therefore has to use the exact reverse order used in the corresponding PUSHV!
The name of the stack may be left blank, like this:
pushv ,var1,var2,var3 . . popv ,var3,var2,var1AS will then use a predefined internal default stack.
AS checks at the end of a pass if there are stacks that are not empty and issues their names together with their ''filling level''. This allows to find out if there are any unpaired PUSHVs or POPVs. However, it is in no case possible to save values in a stack beyond the end of a pass: all stacks are cleared at the beginning of a pass!
valid for: all processors
ORG allows to load the internal address counter (of the assembler) with a new value. The value range depends on the currently selected segment and on the processor type (tables 3.1 to 3.5). The lower bound is always zero, and the upper bound is the given value minus 1.
CAUTION: If the PHASE instruction is also used, one
has to keep in mind that the argument of ORG always is
the load address of the code. Expressions using the $ or *
symbol to refer to the current program counter however deliver
the execution address of the code and do not yeld the
desired result when used as argument for ORG. The
RORG statement (3.2.2) should be
used in such cases.
Target
CODE
DATA
IDATA
XDATA
YDATA
BIT-
DATA IO
REG
ROM-
DATA
68xxx/MCF
4G
---
---
---
---
---
---
---
---
DSP56000/
DSP5630064K/
16M---
---
64K/
16M64K/
16M---
---
---
---
PowerPC
4G
---
---
---
---
---
---
---
---
M*Core
4G
---
---
---
---
---
---
---
---
6800,6301,
681164K
---
---
---
---
---
---
---
---
6805/
HC088K/
64K---
------
------
------
------
------
------
------
---
6809,
630964K
---
---
---
---
---
---
---
---
68HC12(X),
XGATE64K
---
---
---
---
---
---
---
---
S12Z
16M
---
---
---
---
---
---
---
---
68HC16
1M
---
---
---
---
---
---
---
---
68RS08
16K
---
---
---
---
---
---
---
---
H8/300
H8/300H64K
16M---
---
---
---
---
---
---
---
H8/500
(Min)
H8/500
(Max)64K
16M
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
SH7000/
7600/77004G
---
---
---
---
---
---
---
---
HD614023
HD614043
HD6140812K
4K
8K160
256
512---
---
------
---
------
---
------
---
---16
16
16---
---
------
---
---
HD641016
16M
---
---
---
---
---
---
---
---
6502,
MELPS74064K
---
---
---
---
---
---
---
---
HUC6280
2M
---
---
---
---
---
---
---
---
Target |
CODE |
DATA |
IDATA |
XDATA |
YDATA |
BIT- DATA |
IO |
REG |
ROM- DATA |
---|---|---|---|---|---|---|---|---|---|
65816, MELPS- 7700 |
16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
MELPS- 4500 |
8K |
416 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
M16 | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
M16C | 1M | --- | --- | --- | --- | --- | --- | --- | --- |
4004 | 4K | 256 | --- | --- | --- | --- | --- | --- | --- |
8008 | 16K | 8 | --- | --- | --- | --- | --- | --- | --- |
MCS-48, MCS-41 |
4K |
--- |
256 |
256 |
--- |
--- |
--- |
--- |
--- |
MCS-51 | 64K | 256 | 256* | 64K | --- | 256 | --- | --- | --- |
80C390 | 16M | 256 | 256* | 16M | --- | 256 | --- | --- | --- |
MCS-251 | 16M | --- | --- | --- | --- | --- | 512 | --- | --- |
MCS-96 196(N)/ 296 |
64K 16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
8080, 8085 |
64K |
--- |
--- |
--- |
--- |
--- |
256 |
--- |
--- |
80x86, | 64K | 64K | --- | 64K | --- | --- | 64K | --- | --- |
68xx0 | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
8X30x | 8K | --- | --- | --- | --- | --- | --- | --- | --- |
2650 | 8K | --- | --- | --- | --- | --- | --- | --- | --- |
XA | 16M | 16M | --- | --- | --- | --- | 2K- | --- | --- |
AVR | 8K | 64K | --- | --- | --- | --- | 64 | --- | --- |
29XXX | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
* Initial value 80h. As the 8051 does not have any RAM beyond 80h, this value has to be adapted with ORG for the 8051 as target processor! |
|||||||||
+ As the Z180 still can address only 64K logically, the whole address space can only be reached via PHASE instructions! |
|||||||||
- Initial value 400h. |
Target |
CODE |
DATA |
IDATA |
XDATA |
YDATA |
BIT- DATA |
IO |
REG |
ROM- DATA |
---|---|---|---|---|---|---|---|---|---|
80C166, 80C167 |
256K 16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
Z80, Z180, Z380 |
64K 512K+ 4G |
--- |
--- |
--- |
--- |
--- |
256 256 4G |
--- |
--- |
Z8 | 64K | 256 | --- | --- | --- | --- | --- | --- | --- |
eZ8 | 64K | 256 | --- | 64K | --- | --- | --- | --- | --- |
KCPSM | 256 | 256 | --- | --- | --- | --- | --- | --- | --- |
KCPSM3 | 256 | 64 | --- | --- | --- | --- | 256 | --- | --- |
Mico8 | 4096 | 256 | --- | --- | --- | --- | 256 | --- | --- |
TLCS- 900(L) |
16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TLCS-90 | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
TLCS- 870 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TLCS-47 | 64K | 1K | --- | --- | --- | --- | 16 | --- | --- |
TLCS- 9000 |
16M |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TC9331 | 320 | --- | --- | --- | --- | --- | --- | --- | --- |
PIC 16C5x |
2K |
32 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
PIC 16C5x |
2K |
32 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
PIC 16C64, 16C86 |
8K |
512 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
PIC 17C42 |
64K |
256 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
SX20 | 2K | 256 | --- | --- | --- | --- | --- | --- | --- |
ST6 | 4K | 256 | --- | --- | --- | --- | --- | --- | --- |
ST7 | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
STM8 | 16M | --- | --- | --- | --- | --- | --- | --- | --- |
ST9 | 64K | 64K | --- | --- | --- | --- | --- | 256 | --- |
Target |
CODE |
DATA |
IDATA |
XDATA |
YDATA |
BIT- DATA |
IO |
REG |
ROM- DATA |
---|---|---|---|---|---|---|---|---|---|
6804 | 4K | 256 | --- | --- | --- | --- | --- | --- | --- |
32010 32015 |
4K 4K |
144 256 |
--- |
--- |
--- |
--- |
8 8 |
--- |
--- |
320C2x | 64K | 64K | --- | --- | --- | --- | 16 | --- | --- |
320C3x | 16M | --- | --- | --- | --- | --- | --- | --- | --- |
320C40 | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
320C44 | 32M | --- | --- | --- | --- | --- | --- | --- | --- |
320C5x/ 320C20x/ 320C54x |
64K |
64K |
--- |
--- |
--- |
--- |
64K |
--- |
--- |
TMS 9900 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TMS 70Cxx |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
370xxx |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
MSP430 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TMS1000 TMS1200 |
1K |
64 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
TMS1100 TMS1300 |
2K |
128 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
SC/MP | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
807x | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
COP4 | 512 | --- | --- | --- | --- | --- | --- | --- | --- |
COP8 | 8K | 256 | --- | --- | --- | --- | --- | --- | --- |
ACE | 4K* | --- | --- | --- | --- | --- | --- | --- | --- |
F8 | 4K* | 64 | --- | --- | --- | --- | 256 | --- | --- |
µPD 78(C)10 |
64K |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
7566 | 1K | 64 | --- | --- | --- | --- | --- | --- | --- |
7508 | 4K | 256 | --- | --- | --- | --- | 16 | --- | --- |
* initial value 800h resp. 0C00h |
Target |
CODE |
DATA |
IDATA |
XDATA |
YDATA |
BIT- DATA |
IO |
REG |
ROM- DATA |
---|---|---|---|---|---|---|---|---|---|
75K0 | 16K | 4K | --- | --- | --- | --- | --- | --- | --- |
78K0 | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
78K2 | 1M | --- | --- | --- | --- | --- | --- | --- | --- |
78K3 | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
78K4 | 16M* | --- | --- | --- | --- | --- | --- | --- | --- |
7720 |
512 |
128 |
--- |
--- |
--- |
--- |
--- |
--- |
512 |
7725 |
2K |
256 |
--- |
--- |
--- |
--- |
--- |
--- |
1024 |
77230 | 8K | --- | --- | 512 | 512 | --- | --- | --- | 1K |
53C8XX | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
F2MC8L | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
F2MC16L | 16M | --- | --- | --- | --- | --- | --- | --- | --- |
MSM5840 | 2K | 128 | --- | --- | --- | --- | --- | --- | --- |
MSM5842 | 768 | 32 | --- | --- | --- | --- | --- | --- | --- |
MSM58421 MSM58422 |
1.5K |
40 |
--- |
--- |
--- |
--- |
--- |
--- |
--- |
MSM5847 | 1.5K | 96 | --- | --- | --- | --- | --- | --- | --- |
MSM5054 | 1K | 62 | --- | --- | --- | --- | --- | --- | --- |
MSM5055 | 1.75K | 96 | --- | --- | --- | --- | --- | --- | --- |
MSM5056 | 1.75K | 90 | --- | --- | --- | --- | --- | --- | --- |
MSM6051 | 2.5K | 119 | --- | --- | --- | --- | --- | --- | --- |
MN1610 | 64K | --- | --- | --- | --- | --- | 64K | --- | --- |
MN1613 | 256K | --- | --- | --- | --- | --- | 64K | --- | --- |
180x | 64K | --- | --- | --- | --- | --- | 8 | --- | --- |
XS1 | 4G | --- | --- | --- | --- | --- | --- | --- | --- |
1750 | 64K | --- | --- | --- | --- | --- | --- | --- | --- |
* area for program code is limited to 1 MByte |
In case that different variations in a processor family have address spaces of different size, the maximum range is listed for each.
ORG is mostly needed to give the code a new starting address or to put different, non-continuous code parts into one source file. In case there is no explicit other value listet in a table entry, the initial address for this segment (i.e. the start address used without ORG) is 0.
valid for: all processors
RORG modifies the program counter just like ORG, however it does not expect an absolute address as argument. Instead, it expects a relative value (positive or negative) that is added to the current program counter. A possible application of this statement is the reservation of a certain amount of address space, or the use in code parts that are included multiple times (e.g. via macros or includes) and that shall be position-independent. Another application is the use in code that has an execution address different from the load address (i.e. the PHASE statement is used). There is no symbol to refer to the current load address, but it can be referred to indirectly via the RORG statement.
valid for: all processors
This command rules for which processor the further code shall be generated. Instructions of other processor families are not accessible afterwards and will produce error messages!
The processors can roughly be distinguished in families, inside the families different types additionally serve for a detailed distinction:
The differences in this family are additional instructions and addressing modes (starting from the 68020). A small exception is the step to the 68030 that misses two instructions: CALLM and RTM. The three representatives of the 683xx family have the same processor core (a slightly reduced 68020 CPU), however completely different peripherals. MCF5xxx represents various ColdFire variants from Motorola/Freescale/NXP, RISC processors downwardly binary compatible to the 680x0. For the 68040, additional control registers (reachable via MOVEC) and instructions for control of the on-chip MMU and caches were added.
a) 68008 -> 68000 -> 68010 -> 68012 -> MCF5202 -> MCF5204 -> MCF5206 -> MCF5208-> MCF52274 -> MCF52277 -> MCF5307 -> MCF5329 -> MCF5373 -> MCF5407 -> MCF5470 -> MCF5471 -> MCF5472 -> MCF5473 -> MCF5474 -> MCF5475 -> MCF51QM -> 68332 -> 68340 -> 68360 -> 68020 -> 68030 -> 68040
b) 56000 --> 56002 --> 56300While the 56002 only adds instructions for incrementing and decrementing the accumulators, the 56300 core is almost a new processor: all address spaces are enlarged from 64K words to 16M and the number of instructions almost has been doubled.
c) PPC403 -> MPPC403 -> MPC505 -> MPC601 -> MPC821 -> RS6000The PPC403 is a reduced version of the PowerPC line without a floating point unit, which is why all floating point instructions are disabled for him; in turn, some microcontroller-specific instructions have been added which are unique in this family. The GC variant of the PPC403 incorporates an additional MMU and has therefore some additional instructions for its control. The MPC505 (a microcontroller variant without a FPU) only differ in its peripheral registers from the 601 as long as I do not know it better - [70] is a bit reluctant in this respect... The RS6000 line knows a few instructions more (that are emulated on many 601-based systems), IBM additionally uses different mnemonics for their pure workstation processors, as a reminiscence of 370 mainframes...
d) MCORE
e) XGATE
f) 6800 -> 6301 -> 6811While the 6301 only offers a few additional instructions, the 6811 delivers a second index register and much more instructions.
g) 6809/6309 and 6805/68HC(S)08These processors are partially source-code compatible to the other 68xx processors, but they have a different binary code format and a significantly reduced (6805) resp. enhanced (6809) instruction set. The 6309 is a CMOS version of the 6809 which is officially only compatible to the 6809, but inofficially offers more registers and a lot of new instructions (see [46]).
h) 68HC12 --> 68HC12XThe 12X core offers a couple of new instructions, and existing instructions were were enriched with new addressing modes.
i) S912ZVC19F0MKH, S912ZVC19F0MLF,All variants contain the same processor core and the same instruction set, only the on-chip peripherals and the amount of built-in memory (RAM, Flash-ROM, EEPROM) vary from device to device.
S912ZVCA19F0MKH, S912ZVCA19F0MLF,
S912ZVCA19F0WKH, S912ZVH128F2CLQ,
S912ZVH128F2CLL, S912ZVH64F2CLQ,
S912ZVHY64F1CLQ, S912ZVHY32F1CLQ,
S912ZVHY64F1CLL, S912ZVHY32F1CLL,
S912ZVHL64F1CLQ, S912ZVHL32F1CLQ,
S912ZVHL64F1CLL, S912ZVHL32F1CLL,
S912ZVFP64F1CLQ, S912ZVFP64F1CLL,
S912ZVH128F2VLQ, S912ZVH128F2VLL,
S912ZVH64F2VLQ, S912ZVHY64F1VLQ,
S912ZVHY32F1VLQ, S912ZVHY64F1VL,
S912ZVHY32F1VLL, S912ZVHL64F1VLQ
j) 68HC16
k) HD6413308 -> HD6413309These both names represent the 300 and 300H variants of the H8 family; the H version owns a larger address space (16Mbytes instead of 64Kbytes), double-width registers (32 bits), and knows a few more instructions and addressing modes. It is still binary upward compatible.
l) HD6475328 -> HD6475348 -> HD6475368 -> HD6475388These processors all share the same CPU core; the different types are only needed to include the correct subset of registers in the file REG53X.INC.
m) SH7000 -> SH7600 --> SH7700The processor core of the 7600 offers a few more instructions that close gaps in the 7000's instruction set (delayed conditional and relative and indirect jumps, multiplications with 32-bit operands and multiply/add instructions). The 7700 series (also known as SH3) furthermore offers a second register bank, better shift instructions, and instructions to control the cache.
n)HD614023 --> HD614043 --> HD614081These three variants of the HMCS400 series differ by the size of the internal ROM and RAM.
<<<<<<< HEAD o) HD641016This is currently the only target with H16 core.
p) 6502 -> 65(S)C02The CMOS version defines some additional instructions, as well as a number of some instruction/addressing mode combinations were added which were not possible on the 6502. The W65C02S adds two opcodes to the 65C02 instruction set to give more fine-grained control over how to stop the CPU for low power modes. The 65SC02 lacks the bit manipulation instructions of the 65C02. The 65CE02 adds branch instructions with 16-bit displacement, a Z register, a 16 bit stack pointer, a programmable base page, and a couple of new instructions.
-> 65CE02 / W65C02S / 65C19 / MELPS740 / HUC6280 / 6502UNDOC
The 65C19 is not binary upward compatible to the original 6502! Some addressing modes have been replaced by others. Furthermore, this processor contains instruction set extensions that facilitate digital signal processing.
The Mitsubishi micro controllers in opposite expand the 6502 instruction set primarily to bit operations and multiplication / division instructions. Except for the unconditional jump and instructions to increment/decrement the accumulator, the instruction extensions have nothing in common.
For the HuC 6280, the feature that sticks out most is the larger address space of 2 MByte instead of 64 KBytes. This is achieved with a buil-tin banking mechanism. Furthermore, it features some special instructions to communicate with a video processor (this chip was used in video games) and to copy memory areas.
The 6502UNDOC processor type enables access to the "undocumented" 6502 instructions, i.e. the operations that result from the usage of bit combinations in the opcode that are not defined as instructions. The variants supported by AS are listed in the appendix containing processor-specific hints.
q) MELPS7700, 65816Apart from a '16-bit-version' of the 6502's instruction set, these processors both offer some instruction set extensions. These are however orthogonal as they are oriented along their 8-bit predecessors (65C02 resp. MELPS-740). Partially, different mnemonics are used for the same operations.
r) MELPS4500
s) M16
t) M16C
u) 4004 -> 4040Opposed to its predecessor, the 4040 features about a dozen additional machine instructions.
v) 8008 -> 8008NEW Intel redefined the mnemonics around 1975, the second variant reflects this new instruction set. A simultaneous support of both sets was not possible due to mnemonic conflicts.
w) 8021, 8022, 8039, (MSM)80C39, 8048, (MSM)80C48, 8041, 8042, 80C382For the ROM-less versions 8039 and 80C39, the commands which are using the BUS (port 0) are forbidden. The 8021 and 8022 are special versions with a strongly shrinked instruction set, for which the 8022 has two A/D- converters and the necessary control-commands. It is possible to transfer the CMOS-versions with the IDL resp. HALT command into a stop mode with lower current consumption. The 8041 and 8042 have some additional instructions for controlling the bus interface, but in turn a few other commands were omitted. The code address space of 8041, 8042, 8021, and 8022 is not externally extendable, and so AS limits the code segment of these processors to one resp. two kilobytes. The (SAB)80C382 is a variant especially designed by Siemens for usage in telephones. It also knows a HALT instruction, plus ist supports indirect addressing for DJNZ and DEC. In turn, several instructions of the 'generic' 8048 were left out. The OKI variants (MSM...) also feature indirect addressing for DJNZ and DEC, plus enhanced control of power-down modes, plus the full basic MCS-48 instrucion set.
The 87C750 can only access a maximum of 2 Kbytes program memory which is why it lacks the LCALL and LJMP instructions. AS does not make any distinction among the processors in the middle, instead it only stores the different names in the MOMCPU variable (see below), which allows to query the setting with IF instructions. An exception is the 80C504 that has a mask flaw in its current versions. This flaw shows up when an AJMP or ACALL instruction starts at the second last address of a 2K page. AS will automatically use long instructions or issues an error message in such situations. The 80C251 in contrast represents a drastic progress in the the direction 16/32 bits, larger address spaces, and a more orthogonal instruction set. One might call the 80C390 the 'small solution': Dallas Semiconductor modified instruction set and architecture only as far as it was necessary for the 16 Mbytes large address spaces.
x) 87C750 -> 8051, 8052, 80C320, 80C501, 80C502, 80C504, 80515, and 80517 -> 80C390 -> 80C251
y) 8096 -> 80196 -> 80196N -> 80296Apart from a different set of SFRs (which however strongly vary from version to version), the 80196 knows several new instructions and supports a 'windowing' mechanism to access the larger internal RAM. The 80196N family extends the address space to 16 Mbytes and introduces a set of instructions to access addresses beyond 64Kbytes. The 80296 extends the CPU core by instructions for signal processing and a second windowing register, however removes the Peripheral Transaction Server (PTS) and therefore looses again two machine instructions.
z) 8080 -> 8085 -> 8085UNDOCThe 8085 knows the additional commands RIM and SIM for controlling the interrupt mask and the two I/O-pins. The type 8085UNDOC enables additional instructions that are not documented by Intel. These instructions are documented in section 4.20.
aa) 8086 -> 80186 -> V30 -> V35Only new instructions are added in this family. The corresponding 8-bit versions are not mentioned due to their instruction compatibility, so one e.g. has to choose 8086 for an 8088-based system.
ab) 80960
ac) 8X300 -> 8X305The 8X305 features a couple of additional registers that miss on the 8X300. Additionally, it can do new operations with these registers (like direct writing of 8 bit values to peripheral addresses).
ad) XAG1, XAG2, XAG3These processors only differ in the size of their internal ROM which is defined in STDDEFXA.INC.
ae) AT90S1200, AT90S2313, AT90S2323, AT90S233, AT90S2343,The various AVR chip variants mainly differ in the amount of on-chip memory (flash, SRAM, EEPROM) an the set of built-in peripherals (GPIO, timers, UART, A/D converter...). Compared to the AT90... predecessors, the ATmega chip also provide additional instructions, while the ATtinys do not support the multiplication instructions.
AT90S4414, AT90S4433, AT90S4434, AT90S8515,
AT90C8534, AT90S8535, ATTINY4, ATTINY5, ATTINY9,
ATTINY10, ATTINY11, ATTINY12, ATTINY13, ATTINY13A,
ATTINY15, ATTINY20, ATTINY24(A), ATTINY25,
ATTINY26, ATTINY28, ATTINY40, ATTINY44(A),
ATTINY45, ATTINY48, ATTINY84(A), ATTINY85,
ATTINY87, ATTINY88, ATTINY102, ATTINY104,
ATTINY167, ATTINY261, ATTINY261A, ATTINY43U,
ATTINY441, ATTINY461, ATTINY461A, ATTINY828,
ATTINY841, ATTINY861, ATTINY861A, ATTINY1634,
ATTINY2313, ATTINY2313A, ATTINY4313, ATMEGA48,
ATMEGA8, ATMEGA8515, ATMEGA8535, ATMEGA88,
ATMEGA8U2, ATMEGA16U2, ATMEGA32U2,
ATMEGA16U4, ATMEGA32U4, ATMEGA32U6, AT90USB646,
AT90USB647, AT90USB1286, AT90USB1287, AT43USB355,
ATMEGA16, ATMEGA161, ATMEGA162, ATMEGA163,
ATMEGA164, ATMEGA165, ATMEGA168, ATMEGA169,
ATMEGA32, ATMEGA323, ATMEGA324, ATMEGA325,
ATMEGA3250, ATMEGA328, ATMEGA329, ATMEGA3290,
ATMEGA406, ATMEGA64, ATMEGA640, ATMEGA644,
ATMEGA644RFR2, ATMEGA645, ATMEGA6450,
ATMEGA649, ATMEGA6490, ATMEGA103, ATMEGA128,
ATMEGA1280, ATMEGA1281, ATMEGA1284,
ATMEGA1284RFR2, ATMEGA2560, ATMEGA2561
af) AM29245 -> AM29243 -> AM29240 -> AM29000The further one moves to the right in this list, the fewer the instructions become that have to be emulated in software. While e.g. the 29245 not even owns a hardware multiplier, the two representors in the middle only lack the floating point instructions. The 29000 serves as a 'generic' type that understands all instructions in hardware.
ag) 80C166 -> 80C167,80C165,80C16380C167 and 80C165/163 have an address space of 16 Mbytes instead of 256 Kbytes, and furthermore they know some additional instructions for extended addressing modes and atomic instruction sequences. They are 'second generation' processors and differ from each other only in the amount of on-chip peripherals.
ah) Z80 -> Z80UNDOC -> Z180 -> Z380While there are only a few additional instructions for the Z180, the Z380 owns 32-bit registers, a linear address space of 4 Gbytes, a couple of instruction set extensions that make the overall instruction set considerably more orthogonal, and new addressing modes (referring to index register halves, stack relative). These extensions partially already exist on the Z80 as undocumented extensions and may be switched on via the Z80UNDOC variant. A list with the additional instructions can be found in the chapter with processor specific hints.
ai) Z8601, Z8604, Z8608, Z8630, Z8631 -> eZ8The variants with Z8 core only differ in internal memory size and on-chip peripherals, i.e. the choice does not have an effect on the supported instruction set. This is substantially different with the eZ8, which brings along a strongly extended instruction set that is in wide parts only source-level compatible.
aj) KCPSMBoth processor cores are not available as standalone components, they are provided as logic cores for gate arrays made by Xilinx The -3 variant offers a larger address space and some additional instructions. Note that it is not binary upward-compatible!
ak) MICO8_05, MICO8_V3, MICO8_V31Lattice unfortunately changed the machine instructions more than once, so different targets became necessary to provide continued support for older projects. The first variant is the one described in the 2005 manual, the two other ones represent versions 3.0 resp. 3.1.
al) 96C141, 93C141These two processors represent the two variations of the processor family: TLCS-900 and TLCS-900L. The differences of these two variations will be discussed in detail in section 4.28.
am) 90C141
an) 87C00, 87C20, 87C40, 87C70The processors of the TLCS-870 series have an identical CPU core, but different peripherals depending on the type. In part registers with the same name are located at different addresses. The file STDDEF87.INC uses, similar to the MCS-51-family, the distinction possible by different types to provide the correct symbol set automatically.
ao) 47C00 -> 470C00 -> 470AC00These three variations of the TLCS-47-family have on-chip RAM and ROM of different size, which leads to several bank switching instructions being added or suppressed.
ap) 97C241
aq) TC9331
ar) 16C54 -> 16C55 -> 16C56 -> 16C57These processors differ by the available code area, i.e. by the address limit after which AS reports overruns.
as) 16C84, 16C64Analog to the MCS-51 family, no distinction is made in the code generator, the different numbers only serve to include the correct SFRs in STDDEF18.INC.
at) 17C42
au) SX20, SX28The SX20 uses a smaller housing and lacks port C.
av) ST6200, ST6201, ST6203, ST6208, ST6209,The various ST6 derivates differ in the amount of on-chip peripherals and built-in memory.
ST6210, ST6215, ST6218, ST6220, ST6225,
ST6228, ST6230, ST6232, ST6235, ST6240,
ST6242, ST6245, ST6246, ST6252, ST6253,
ST6255, ST6260, ST6262, ST6263, ST6265,
ST6280, ST6285
aw) ST7The STM8 core extends the address space to 16 Mbytes and introduces a couple of new instructions. Though many instructions have the same machine code as for ST7, it is not binary upward compatible.
ST72251G1, ST72251G2, ST72311J2, ST72311J4,
ST72321BR6, ST72321BR7, ST72321BR9, ST72325S4,
ST72325S6, ST72325J7, ST72325R9, ST72324J6,
ST72324K6, ST72324J4, ST72324K4, ST72324J2,
ST72324JK21, ST72325S4, ST72325J7, ST72325R9,
ST72521BR6, ST72521BM9, ST7232AK1, ST7232AK2,
ST7232AJ1, ST7232AJ2, ST72361AR4, ST72361AR6,
ST72361AR7, ST72361AR9, ST7FOXK1, ST7FOXK2,
ST7LITES2Y0, ST7LITES5Y0, ST7LITE02Y0,
ST7LITE05Y0, ST7LITE09Y0
ST7LITE10F1, ST7LITE15F1, ST7LITE19F1,
ST7LITE10F0, ST7LITE15F0, ST7LITE15F1,
ST7LITE19F0, ST7LITE19F1,
ST7LITE20F2, ST7LITE25F2, ST7LITE29F2,
ST7LITE30F2, ST7LITE35F2, ST7LITE39F2,
ST7LITE49K2,
ST7MC1K2, ST7MC1K4, ST7MC2N6, ST7MC2S4,
ST7MC2S6, ST7MC2S7, ST7MC2S9, ST7MC2R6,
ST7MC2R7, ST7MC2R9, ST7MC2M9,
STM8
STM8S001J3, STM8S003F3, STM8S003K3, STM8S005C6,
STM8S005K6, STM8S007C8, STM8S103F2, STM8S103F3,
STM8S103K3, STM8S105C4, STM8S105C6, STM8S105K4,
STM8S105K6, STM8S105S4, STM8S105S6, STM8S207MB,
STM8S207M8, STM8S207RB, STM8S207R8, STM8S207R6,
STM8S207CB, STM8S207C8, STM8S207C6, STM8S207SB,
STM8S207S8, STM8S207S6, STM8S207K8, STM8S207K6,
STM8S208MB, STM8S208RB, STM8S208R8, STM8S208R6,
STM8S208CB, STM8S208C8, STM8S208C6, STM8S208SB,
STM8S208S8, STM8S208S6, STM8S903K3, STM8S903F3,
STM8L050J3, STM8L051F3, STM8L052C6, STM8L052R8,
STM8L001J3, STM8L101F1, STM8L101F2, STM8L101G2,
STM8L101F3, STM8L101G3, STM8L101K3, STM8L151C2,
STM8L151K2, STM8L151G2, STM8L151F2, STM8L151C3,
STM8L151K3, STM8L151G3, STM8L151F3, STM8L151C4,
STM8L151C6, STM8L151K4, STM8L151K6, STM8L151G4,
STM8L151G6, STM8L152C4, STM8L152C6, STM8L152K4,
STM8L152K6, STM8L151R6, STM8L151C8, STM8L151M8,
STM8L151R8, STM8L152R6, STM8L152C8, STM8L152K8,
STM8L152M8, STM8L152R8, STM8L162M8, STM8L162R8,
STM8AF6366, STM8AF6388, STM8AF6213, STM8AF6223,
STM8AF6226, STM8AF6246, STM8AF6248, STM8AF6266,
STM8AF6268, STM8AF6269, STM8AF6286, STM8AF6288,
STM8AF6289, STM8AF628A, STM8AF62A6, STM8AF62A8,
STM8AF62A9, STM8AF62AA, STM8AF5268, STM8AF5269,
STM8AF5286, STM8AF5288, STM8AF5289, STM8AF528A,
STM8AF52A6, STM8AF52A8, STM8AF52A9, STM8AF52AA,
STM8AL3136, STM8AL3138, STM8AL3146, STM8AL3148,
STM8AL3166, STM8AL3168, STM8AL3L46, STM8AL3L48,
STM8AL3L66, STM8AL3L68, STM8AL3188, STM8AL3189,
STM8AL318A, STM8AL3L88, STM8AL3L89, STM8AL3L8A,
STM8TL52F4, STM8TL52G4, STM8TL53C4, STM8TL53F4,
STM8TL53G4
ax) ST9020, ST9030, ST9040, ST9050These 4 names represent the four ''sub-families'' of the ST9 family, which only differ in their on-chip peripherals. Their processor cores are identical, which is why this distinction is again only used in the include file containing the peripheral addresses.
ay) 6804
az) 32010->32015The TMS32010 owns just 144 bytes of internal RAM, and so AS limits addresses in the data segment just up to this amount. This restriction does not apply for the 32015, the full range from 0..255 can be used.
ba) 320C25 -> 320C26 -> 320C28These processors only differ slightly in their on-chip peripherals and in their configuration instructions.
bb) 320C30, 320C31 -> 320C40, 320C44The 320C31 is a reduced version with the same instruction set, however fewer peripherals. The distinction is exploited in STDDEF3X.INC. The C4x variants are sourcecode upward compatible, the machine codes of some instructions are however slightly different. Once again, the C44 is a stripped-down version of the C40, with less peripherals and a smaller address space.
bc) 320C203 -> 320C50, 320C51, 320C53The first one represents the C20x family of signal processors which implement a subset of the C5x instruction set. The distinction among the C5x processors is currently not used by AS.
bd) 320C541This one at the moment represents the TMS320C54x family...
be) TMS9900
All members of this family share the same CPU core, they therefore do not differ in their instruction set. The differences manifest only in the file REG7000.INC where address ranges and peripheral addresses are defined. Types listed in the same row have the same amount of internal RAM and the same on-chip peripherals, they differ only in the amount of integrated ROM.
bf) TMS70C00, TMS70C20, TMS70C40, TMS70CT20, TMS70CT40, TMS70C02, TMS70C42, TMS70C82, TMS70C08, TMS70C48
bg) 370C010, 370C020, 370C030, 370C040 and 370C050Similar to the MCS-51 family, the different types are only used to differentiate the peripheral equipment in STDDEF37.INC; the instruction set is always the same.
bh) MSP430 -> MSP430X The X variant of the CPU core extends the address space from 64 KiBytes to 1 MiByte and augments the instruction set, e.g. by prefixed to repeat instructions.
bi) TMS1000, TMS1100, TMS1200, TMS1300TMS1000 and TMS1200 each provide 1 KByte of ROM and 64 nibbles of RAM, while TMS1100 and TMS1300 provide twice the amount of RAM and ROM. Furthermore, TI has defined a significantly different default instruction set fot TMS1100 and TMS1300(AS only knows the default instruction sets!)
bj) SC/MP
bk) 8070This processor represents the whole 807x family (which consists at least of the 8070, 8072, and 8073), which however shares identical CPU cores.
bl) COP87L84This is the only member of National Semiconductor's COP8 family that is currently supported. I know that the family is substantially larger and that there are representors with differently large instruction sets which will be added when a need occurs. It is a beginning, and National's documentation is quite extensive...
bm) COP410 -> COP420 -> COP440 -> COP444 The COP42x derivates offer some additional instructions, plus other instructions have an extended operand range.
This series of DECT controllers differentiates itself by the amount of instructions, since each of them supports different B field formats and their architecture has been optimized over time.
bn) SC14400, SC14401, SC14402, SC14404, SC14405, SC14420, SC14421, SC14422, SC14424
bo) ACE1101, ACE1202
bp) MK3870, MK3872, MK3873, MK3874, MK3875, MK3876,The instruxction st of all F8 variants is the same, except for the CMOS version than knows two new instructions (HET and HAL). Otherwise, they only differ by the amount of built-in memory (2 or 4 Kbytes of ROM, 64 bytes of executable RAM or not) and the integrated peripherals (with or without serial port).
MK38C70
bq) 7810->78C10The NMOS version has no stop-mode; the respective command and the ZCM register are omitted. CAUTION! NMOS and CMOS version partially differ in the reset values of some registers!
br) 7500 <-> 7508There are two different types of CPU cores in the µPD75xx family: the 7566 represents the the 'instruction set B', which provides less instructions, less registers and smaller address spaces. The 7508 represents the 'full' instruction set A. CAUTION! These instruction sets are not 100% binary compatible!
This 'cornucopia' of processors differs only by the RAM size in one group; the groups themselves again differ by their on-chip peripherals on the one hand and by their instruction set's power on the other hand.
bs) 75402, 75004, 75006, 75008, 75268, 75304, 75306, 75308, 75312, 75316, 75328, 75104, 75106, 75108, 75112, 75116, 75206, 75208, 75212, 75216, 75512, 75516
bt) 78070This is currently the only member of NEC's 78K0 family I am familiar with. Similar remarks like for the COP8 family apply!
bu) 78214This is currently the representor of NEC's 78K2 family.
bv) 78310This is currently the representor of NEC's 78K3 family.
bw) 784026This is currently the representor of NEC's 78K4 family.
bx) 7720 -> 7725The µPD7725 offers larger address spaces and som more instructions compared to his predecessor. CAUTION! The processors are not binary compatible to each other!
by) 77230
The simpler members of this family of SCSI processors lack some instruction variants, furthermore they are different in their set of internal registers.
bz) SYM53C810, SYM53C860, SYM53C815, SYM53C825, SYM53C875, SYM53C895
ca) MB89190This processor type represents Fujitsu's F2MC8L series...
cb) MB9500...just like this one does it currently for the 16-bit variants from Fujitsu!
cc) MSM5840, MSM5842, MSM58421, MSM58422, MSM5847These variants of the OLMS-40 family differ in their instruction set and in the amount of internal program and data memory.
cd) MSM5054, MSM5055, MSM5056, MSM6051, MSM6052The as for the OLMS-40 family: differences in instruction set and the amount of internal program and data memory.
ce) MN1610[ALT] -> MN1613[ALT]In addition to its predecessor's features, the MN1613 offers a larger address space, a floating point unit and a couple of new machine instructions.
ce) 1802 -> 1804, 1805, 1806 -> 1804A, 1805A, 1806A1804, 1805, and 1806 feature an instruction set that is slightly enhanced, compared to the 'original' 1802, plus on-chip RAM and an integrated timer. The A variants extend the instruction set by DSAV, DBNZ, and instructions for addition and subtraction in BCD format.
cf) XS1This type represents the XCore-"family".
cg) 1750MIL STD 1750 is a standard, therefore there is only one (standard) variant...
The CPU instruction needs the processor type as a simple literal, a calculation like:
CPU 68010+10is not allowed. Valid calls are e.g.
CPU 8051or
CPU 6800Regardless of the processor type currently set, the integer variable MOMCPU contains the current status as a hexadecimal number. For example, MOMCPU=$68010 for the 68010 or MOMCPU=80C48H for the 80C48. As one cannot express all letters as hexadecimal digits (only A..F are possible), all other letters must must be omitted in the hex notation; for example, MOMCPU=80H for the Z80.
You can take advantage of this feature to generate different code depending on the processor type. For example, the 68000 does not have a machine instruction for a subroutine return with stack correction. With the variable MOMCPU you can define a macro that uses the machine instruction or emulates it depending on the processor type:
myrtd macro disp if MOMCPU<$68010 ; emulate for 68008 & 68000 move.l (sp),disp(sp) lea disp(sp),sp rts elseif rtd #disp ; direct use on >=68010 endif endm cpu 68010 myrtd 12 ; results in RTD #12 cpu 68000 myrtd 12 ; results in MOVE../LEA../RTSAs not all processor names are built only out of numbers and letters from A..F, the full name is additionally stored in the string variable named MOMCPUNAME.
The assembler implicitly switches back to the CODE segment when a CPU instruction is executed. This is done because CODE is the only segment all processors support.
The default processor type is 68008, unless it has been changed via the command line option with same name.
valid for: 680x0, FPU also for 80x86, i960, SUPMODE also for TLCS-900, SH7000, i960, 29K, XA, PowerPC, M*Core, and TMS9900
These three switches allow to define which parts of the instruction set shall be disabled because the necessary preconditions are not valid for the following piece of code. The parameter for these instructions may be either ON or OFF, the current status can be read out of a variable which is either TRUE or FALSE.
The commands have the following meanings in detail:
valid for: 680x0
Motorola integrated the MMU into the processor starting with the 68030, but the built-in FPU is equipped only with a relatively small subset of the 68851 instruction set. AS will therefore disable all extended MMU instructions when the target processor is 68030 or higher. It is however possible that the internal MMU has been disabled in a 68030-based system and the processor operates with an external 68851. One can the use a FULLPMMU ON to tell AS that the complete MMU instruction set is allowed. Vice versa, one may use a FULLPMMU OFF to disable all additional instruction in spite of a 68020 target platform to assure that portable code is written. The switch between full and reduced instruction set may be done as often as needed, and the current setting may be read from a symbol with the same name. CAUTION! The CPU instruction implicitly sets or resets this switch when its argument is a 68xxx processor! FULLPMMU therefore has to be written after the CPU instruction!
valid for: 680x0, 68xx, M*Core, XA, H8, SH7000, MSP430(X), TMS9900, ST7/STM8
Processors of the 680x0 family are quite critical regarding odd addresses: instructions must not start on an odd address, and data accesses to odd addresses are only allowed bytewise up to the 68010. The H8/300 family simply resets the lowest address bit to zero when accessing odd addresses, the 500 in contrast 'thanks' with an exception... AS therefore tries to round up data structures built with DC or DS to an even number of bytes. This however means for DC.B and DS.B that a padding byte may have to be added. This behaviour can be turned on and off via the PADDING instruction. Similar to the previous instructions, the argument may be either ON or OFF, and the current setting may be read from a symbol with the same name. PADDING is by default only enabled for the 680x0 family, it has to be turned on explicitly for all other families!
valid for: AVR
In some way, PACKING is similar to PADDING, it just has a somewhat opposite effect: While PADDING extends the disposed data to get full words and keep a possible alignment, PACKING squeezes several values into a single word. This makes sense for the AVR's code segment since the CPU has a special instruction ( LPM) to access single bytes within a 16-bit word. In case this option is turned on (argument ON), two byte values are packed into a single word by DATA, similar to the single characters of string arguments. The value range of course reduces to -128...+255. If this option is turned off (argument OFF), each integer argument obtains its own word and may take values from -32768...+65535.
This distinctin is only made for integer arguments of DATA, strings will always be packed.. Keep further in mind that packing of values only works within the arguments of a DATA statement; if one has subsequent DATA statements, there will still be half-filled words when the argument count is odd!
valid for: TLCS-900, H8
The processors of the TLCS-900-family are able to work in 2 modes, the minimum and maximum mode. Depending on the actual mode, the execution environment and the assembler are a little bit different. Along with this instruction and the parameter ON or OFF, AS is informed that the following code will run in maximum resp. minimum mode. The actual setting can be read from the variable INMAXMODE. Presetting is OFF, i.e. minimum mode.
Similarly, one uses this instruction to tell AS in H8 mode whether the address space is 64K or 16 Mbytes. This setting is always OFF for the 'small' 300 version and cannot be changed.
valid for: Z380
The Z380 may operate in altogether 4 modes, which are the result of setting two flags: The XM flag rules whether the processor shall operate wit an address space of 64 Kbytes or 4 Gbytes and it may only be set to 1 (after a reset, it is set to 0 for compatibility with the Z80). The LW flag in turn rules whether word operations shall work with a word size of 16 or 32 bits. The setting of these two flags influences range checks of constants and addresses, which is why one has to tell AS the setting of these two flags via these instructions. The default assumption is that both flags are 0, the current setting (ON or OFF) may be read from the predefined symbols INEXTMODE resp. INLWORDMODE.
valid for: MCS-251
Intel substantially extended the 8051 instruction set with the 80C251, but unfortunately there was only a single free opcode for all these new instructions. To avoid a processor that will be eternally crippled by a prefix, Intel provided two operating modes: the binary and the source mode. The new processor is fully binary compatible to the 8051 in binary mode, all new instructions require the free opcode as prefix. In source mode, the new instructions exchange their places in the code tables with the corresponding 8051 instructions, which in turn then need a prefix. One has to inform AS whether the processor operates in source mode (ON) or binary mode (OFF) to enable AS to add prefixes when required. The current setting may be read from the variable INSRCMODE. The default is OFF.
valid for: MCS-51/251, PowerPC
Intel broke with its own principles when the 8051 series was designed: in contrast to all traditions, the processor uses big-endian ordering for all multi-byte values! While this was not a big deal for MCS-51 processors (the processor could access memory only in 8-bit portions, so everyone was free to use whichever endianess one wanted), it may be a problem for the 251 as it can fetch whole (long-)words from memory and expects the MSB to be first. As this is not the way of constant disposal earlier versions of AS used, one can use this instruction to toggle between big and little endian mode for the instructions DB, DW, DD, DQ, and DT. BIGENDIAN OFF (the default) puts the LSB first into memory as it used to be on earlier versions of AS, BIGENDIAN ON engages the big-endian mode compatible to the MCS-251. One may of course change this setting as often as one wants; the current setting can be read from the symbol with the same name.
valid for: Atmel AVR
After this switch has been set to ON, AS will assume that the processor's program counter does not have the full length of 16 bits given by the architecture, but instead a length that is exactly sufficient to address the internal ROM. For example, in case of the AT90S8515, this means 12 bits, corresponding to 4 Kwords or 8 Kbytes. This assumption allows relative branches from the ROM's beginning to the end and vice versa which would result in an out-of-branch error when using strict arithmetics. Here, they work because the carry bits resulting from the target address computation are discarded. Assure that the target processor you are using works in the outlined way before you enable this option! In case of the abovementioned AT90S8515, this option is even necessary because it is the only way to perform a direct jump through the complete address space...
This switch is set to OFF by default, and its current setting may be read from a symbol with same name.
valid for: all processors
Some microcontrollers and signal processors know various address ranges, which do not overlap with each other and require also different instructions and addressing modes for access. To manage these ones also, the assembler provides various program counters, you can switch among them to and from by the use of the SEGMENT instruction. For subroutines included with INCLUDE, this e.g. allows to define data used by the main program or subroutines near to the place they are used. In detail, the following segments with the following names are supported:
The bit segment is managed as if it would be a byte segment, i.e. the addresses will be incremented by 1 per bit.
Labels get the same type as attribute as the segment that was active when the label was defined. So the assembler has a limited ability to check whether you access symbols of a certain segment with wrong instructions. In such cases the assembler issues a warning.
Example:
CPU 8051 ; MCS-51-code segment code ; test code setb flag ; no warning setb var ; warning : wrong segment segment data var db ? segment bitdata flag db ?
valid for: all processors
For some applications (especially on Z80 systems), the code must be moved to another address range before execution. If the assembler didn't know about this, it would align all labels to the load address (not the start address). The programmer is then forced to write jumps within this area either independent of location or has to add the offset at each symbol manually. The first one is not possible for some processors, the last one is extremely error-prone. With the commands PHASE and DEPHASE, it is possible to inform the assembler at which address the code will really be executed on the target system:
phase <address>informs the assembler that the following code shall be executed at the specified address. The assembler calculates thereupon the difference to the real program counter and adds this difference for the following operations:
DEPHASE, this ''shifting'' is reverted to the value previous to the most recent PHASE instruction. PHASE und DEPHASE may be used in a nested manner.
The assembler keeps phase values for all defined segments, although this instruction pair only makes real sense in the code segment.
valid for: all processors
The command SAVE forces the assembler to push the contents of following variables onto an internal stack:
SAVE ; save old status LISTING OFF ; save paper . ; the actual code . RESTORE ; restoreIn opposite to a simple LISTING OFF .. ON-pair, the correct status will be restored, in case the listing generation was switched off already before.
The assembler checks if the number of SAVE-and RESTORE-commands corresponds and issues error messages in the following cases:
valid for: various
This instruction allows to tell AS the current setting of certain registers whose contents cannot be described with a simple ON or OFF. These are typically registers that influence addressing modes and whose contents are important to know for AS in order to generate correct addressing. It is important to note that ASSUME only informs AS about these, no machine code is generated that actually loads these values into the appropriate registers!
A value defined with ASSUME can be queried or integrated into expressions via the built-in function ASSUMEDVAL. This is the case for all architectures listed in the following sub-sections except for the 8086.
The 65CE02 features a a register named 'B' that is used to set the 'base page'. In comparison to the original 6502, this allows the programmer to place the memory page addressable with short (8 bit) addresses anywhere in the 64K address space. This register is set to zero after a reset, so the 65CE02 behaves like its predecessor. A base page at zero is also the default assumption of the assembler. It may be informed about its actual contents via a ASSUME B:xx statement. Addresses located in this page will then automatically be addressed via short addressing modes.
In contrast to its 'predecessors' like the 6800 and 6502, the position of the direct page, i.e. the page of memory that can be reached with single-byte addresses, can be set freely. This is done via the 'direct page register' that sets the page number. One has to assign a corresponding value to this register via ASSUME is the contents are different from the default of 0, otherwise wrong addresses will be generated!
Also for the HC11, the designers finally weren't able to avoid the major sin: using a banking scheme to address more than 64 Kbytes with only 16 address lines. The registers MMSIZ, MMWBR, MM1CR, and MM2CR control whether and how the additional 512K address ranges are mapped into the physical address space. AS intially assumes the reset state of these registers, i.e. all are set to $00 and windowing is disabled.
Similar to its cousin without the appended 'X', the HC12X supports a short direct addressing mode. In this case however, it can be used to address more than just the first 256 bytes of the address space. The DIRECT register specifices which 256 byte page of the address space is addressed by this addressing mode. ASSUME is used to tell AS the current value of this register, so it is able to automatically select the most efficient address ing mode when absolute addresses are used. The default is 0, which corresponds to the reset state.
The 68HC16 employs a set of bank registers to address a space of 1 Mbyte with its registers that are only 16 bits wide. These registers supply the upper 4 bits. Of these, the EK register is responsible for absolute data accesses (not jumps!). AS checks for each absolute address whether the upper 4 bits of the address are equal to the value of EK specified via ASSUME. AS issues a warning if they differ. The default for EK is 0.
In maximum mode, the extended address space of these processors is addressed via a couple of bank registers. They carry the names DP (registers from 0..3, absolute addresses), EP (register 4 and 5), and TP (stack). AS needs the current value of DP to check if absolute addresses are within the currently addressable bank; the other two registers are only used for indirect addressing and can therefore not be monitored; it is a question of personal taste whether one specifies their values or not. The BR register is in contrast important because it rules which 256-byte page may be accessed with short addresses. It is common for all registers that AS does not assume any default value for them as they are undefined after a CPU reset. Everyone who wants to use absolute addresses must therefore assign values to at least DR and DP!
Microcontrollers of this series know a ''special page'' addressing mode for the JSR instruction that allows a shorter coding for jumps into the last page of on-chip ROM. The size of this ROM depends of course on the exact processor type, and there are more derivatives than it would be meaningful to offer via the CPU instruction...we therefore have to rely on ASSUME to define the address of this page, e.g.
ASSUME SP:$1fin case the internal ROM is 8K.
These processors contain a lot of registers whose contents AS has to know in order to generate correct machine code. These are the registers in question:
name | function | value range | default |
---|---|---|---|
DT/DBR PG/PBR DPR X M |
data bank code Bank directly addr. page index register width accumulator width |
0-$ff 0-$ff 0-$ffff 0 or 1 0 or 1 |
0 0 0 0 0 |
To avoid endless repetitions, see section 4.13 for instructions how to use these registers. The handling is otherwise similar to the 8086, i.e. multiple values may be set with one instruction and no code is generated that actually loads the registers with the given values. This is again up to the programmer!
Starting with the 80196, all processors of the MCS-96 family have a register 'WSR' that allows to map memory areas from the extended internal RAM or the SFR range into areas of the register file which may then be accessed with short addresses. If one informs AS about the value of the WSR register, it can automatically find out whether an absolute address can be addressed with a single-byte address via windowing; consequently, long addresses will be automatically generated for registers covered by windowing. The 80296 contains an additional register WSR1 to allow simultaneous mapping of two memory areas into the register file. In case it is possible to address a memory cell via both areas, AS will always choose the way via WSR!
For indirect addressing, displacements may be either short (8 bits, -128 to +127) or long (16 bits). The assembler will automatically use the shortest possible encoding for a given displacement. It is however possible to enforce a 16-bit coding by prefixing the displacement argument with a bigger sign ((>). Similarly, absolute addresses in the area from 0ff80h to 0ffffh may be reached via a short offset relative to the "null register".
The 8086 is able to address data from all segments in all instructions, but it however needs so-called ''segment prefixes'' if another segment register than DS shall be used. In addition it is possible that the DS register is adjusted to another segment, e.g. to address data in the code segment for longer parts of the program. As AS cannot analyze the code's meaning, it has to informed via this instruction to what segments the segment registers point at the moment, e.g.:
ASSUME CS:CODE, DS:DATA .It is possible to assign assumptions to all four segment registers in this way. This instruction produces no code, so the program itself has to do the actual load of the registers with the values.
The usage of this instruction has on the one hand the result that AS is able to automatically put ahead prefixes at sporadic accesses into the code segment, or on the other hand, one can inform AS that the DS-register was modified and you can save explicit CS:-instructions.
Valid arguments behind the colon are CODE, DATA and NOTHING. The latter value informs AS that a segment register contains no usable value (for AS). The following values are preinitialized:
CS:CODE, DS:DATA, ES:NOTHING, SS:NOTHING
The XA family has a data address space of 16 Mbytes, a process however can always address within a 64K segment only that is given by the DS register. One has to inform AS about the current value of this register in order to enable it to check accesses to absolute addresses.
The processors of the 29K family feature a register RBP that allows to protect banks of 16 registers against access from user mode. The corresponding bit has to be set to achieve the protection. ASSUME allows to tell AS which value RBP currently contains. AS can warn this way in case a try to access protected registers from user mode is made.
Though none of the 80C166/167's registers is longer than sixteen bits, this processor has 18/24 address lines and can therefore address up to 256Kbytes/16Mbytes. To resolve this contradiction, it neither uses the well-known (and ill-famed) Intel method of segmentation nor does it have inflexible bank registers...no, it uses paging! To accomplish this, the logical address space of 64 Kbytes is split into 4 pages of 16 Kbytes, and for each page there is a page register (named DPP0..DPP3) that rules which of the 16/1024 physical pages shall be mapped to this logical page. AS always tries to present the address space with a size of 256Kbytes/16MBytes in the sight of the programmer, i.e. the physical page is taken for absolute accesses and the setting of bits 14/15 of the logical address is deduced. If no page register fits, a warning is issued. AS assumes by default that the four registers linearly map the first 64 Kbytes of memory, in the following style:
ASSUME DPP0:0,DPP1:1,DPP2:2,DPP3:3The 80C167 knows some additional instructions that can override the page registers' function. The chapter with processor-specific hints describes how these instructions influence the address generation.
Some machine instructions have a shortened form that can be used if the argument is within a certain range:
The direct data address space of these processors (it makes no difference whether you address directly or via the HL register) has a size of only 256 nibbles. Because the ''better'' family members have up to 1024 nibbles of RAM on chip, Toshiba was forced to introduce a banking mechanism via the DMB register. AS manages the data segment as a continuous addressing space and checks at any direct addressing if the address is in the currently active bank. The bank AS currently expects can be set by means of
ASSUME DMB:<0..3>The default value is 0.
The microcontrollers of the ST62 family are able to map a part (64 bytes) of the code area into the data area, e.g. to load constants from the ROM. This means also that at one moment only one part of the ROM can be addressed. A special register rules which part it is. AS cannot check the contents of this register directly, but it can be informed by this instruction that a new value has been assigned to the register. AS then can test and warn if necessary, in case addresses of the code segment are accessed, which are not located in the ''announced'' window. If, for example, the variable VARI has the value 456h, so
ASSUME ROMBASE:VARI>>6sets the AS-internal variable to 11h, and an access to VARI generates an access to address 56h in the data segment.
It is possible to assign a simple NOTHING instead of a value, e.g. if the bank register is used temporarily as a memory cell. This value is also the default.
The program counter of these controller only has a width of 12 bits. This means that some sort of banking scheme had to be introduced if a device includes more than 4 KBytes of program memory. The banking scheme splits both proram space and program memory in pages of 2 KBytes. Page one of the program space always accesses page one of program memory. The PRPR register present on such devices selects which page of program memory is accessed via addresses 000h to 7ffh of program space. As an initial approcimation, AS regards program space to be linear and of the size of program memory. If a jump or call from page one is made to code in one of the other pages, it checks whether the assumed contents of the PRPR register match the destination address. If a jump or call is done from one of the other pages to an address outside of page one, it checks whether the destination address is within the same page. IMPORTANT: The program counter itself is only 12 bits wide. It is therefore not possible to jump from one page to another one, without an intermediate step of jumping back to page one. Changing the PRPR register while operating outside of page one would result in ''pulling out'' the code from under one's feet.
The ST9 family uses exactly the same instructions to address code and data area. It depends on the setting of the flag register's DP flag which address space is referenced. To enable AS to check if one works with symbols from the correct address space (this of course only works with absolute accesses!), one has to inform AS whether the DP flag is currently 0 (code) or 1 (data). The initial value of this assumption is 0.
78K2 is an 8/16 bit architecture, which has later been extended to a one-megabyte addres space via banking. Banking is realized with the registers PM6 (normal case) resp. P6 (alternate case with & as prefix) that supply the missing upper four address bits. At least for absolute addresses, AS can check whether the current, linear 20-bit address is within the given 64K window.
Processors witrh a 78K3 core have register banks that consist of 16 registers. These registers may be used via their numbers (R0 to R15) or their symbolic names (X=R0, A=R1, C=R2, B=R3, VPL=R8, VPH=R9, UPL=R10, UPH=R11, E=R12, D=R13, L=R14, H=R15). The processor core has a register select bit (RSS) to switch the mapping of A/X and B/C from R0..R3 to R4..R7. This is mainly important for instructions that implicitly use one of these registers (i.e. instruction that do not encode the register number in the machine code). However, it is also possible to inform the assembler about the changed mapping via a
assume rss:1
The assmebler will then insert the alternate register numbers into machine instructions that explicitly encode the register numbers. Vice versa, R5 will be treated like A instead of R1 in the source code.
78K4 was designed as an 'upgrade path' from 78K3, which is why this processor core contains the same RSS bit to control the mapping of registers AX and BC (though NEC discourages use of it in new code).
Aside from many new instructins and addressing modes, the most significant extension is the larger address space of 16 MBytes, of which only the first MByte may be used for program code. The CPU-internal RAM and all special function registers may be positioned either at the top of the first MByte or the top of the first 64 KByte page. Choice is made via the LOCATION machine instruction that either takes a 0 or 15 as argument. Together with remapping RAM and SFRs, the processor also switches the address ranges that may be reached with short (8 bit) addresses. Parallel to using LOCATION, one has to inform the assembler about this setting via a ASSUME LOCATION:.. statement. It will then use short addressing for the proper ranges. The assembler will assume a default of 0 for LOCATION.
As all instruction words of this processor family are only 32 bits long (of which only 16 bits were reserved for absolute addresses), the missing upper 8/16 bits have to be added from the DP register. It is however still possible to specify a full 24/32-bit address when addressing, AS will check then whether the upper 8 bits are equal to the DP register's assumed values. ASSUME is different to the LDP instruction in the sense that one cannot specify an arbitrary address out of the bank in question, one has to extract the upper bits by hand:
ldp @addr assume dp:addr>>16 . . ldi @addr,r2
These processors have a register (V) that allows to move the ''zero page'', i.e. page of memory that is addressable by just one byte, freely in the address space, within page limits. By reasons of comforts you don't want to work with expressions such as
inrw Lo(counter)so AS takes over this job, but only under the premise that it is informed via the ASSUME-command about the contents of the V register. If an instruction with short addressing is used, it will be checked if the upper half of the address expression corresponds to the expected content. A warning will be issued if both do not match.
As the whole address space of 12 bits could not be addressed even by the help of register pairs (8 bits), NEC had to introduce banking (like many others too...): the upper 4 address bits are fetched from the MBS register (which can be assigned values from 0 to 15 by the ASSUME instruction), which however will only be regarded if the MBE flag has been set to 1. If it is 0 (default), the lowest and highest 128 nibbles of the address space can be reached without banking. The ASSUME instruction is undefined for the 75402 as it contains neither a MBE flag nor an MBS register; the initial values cannot be changed therefore.
Similar to many other families of microcontrollers, this family suffers somewhat from its designers miserliness: registers of only 16 bits width are faced with an address space of 24 bits. Once again, bank registers had to fill the gap. In detail, these are PCB for the progam code, DTB for all data accesses, ADB for indirect accesses via RW2/RW6, and SSB/USB for the stacks. They may all take values from 0 to 255 and are by default assumed to be 0, with the exception of 0ffh for PCB.
Furthermore, a DPR register exists that specifies which memory page within the 64K bank given by DTB may be reached with 8 bit addresses. The default for DPR is 1, resulting in a default page of 0001xxh when one takes DTB's default into account.
The MN1613 is an extension of an architecture with 16 bit addresses. The address extension is done by a set of "segment registers" (CSBR, SSBR, TSR0, and TSR1), each of which is four bits wide. The contents of a segment register, left-shifted by 14 bits, is added to the 16 bit addresses. This way, a process may access a memory window of 64 KWords within the address space of 256 KWords. The assembler uses segment register values reported via ASSUME to warn whether an absolute address is outside the window defined by the used segment register. If the address is within the window, it will compute the correc t16-bit offset. Naturally, this cannot be done when indirect addressing is used.
valid for: 29K
AMD defined the 29000's series exception handling for undefined instructions in a way that there is a separate exception vector for each instruction. This allows to extend the instruction set of a smaller member of this family by a software emulation. To avoid that AS quarrels about these instructions as being undefined, the EMULATED instruction allows to tell AS that certain instructions are allowed in this case. The check if the currently set processors knows the instruction is then skipped. For example, if one has written a module that supports 32-bit IEEE numbers and the processor does not have a FPU, one writes
EMULATED FADD,FSUB,FMUL,FDIV EMULATED FEQ,FGE,FGT,SQRT,CLASS
valid for: XA
BRANCHEXT with either ON or OFF as argument tells AS whether short branches that are only available with an 8-bit displacement shall automatically be 'extended', for example by replacing a single instruction like
bne targetwith a longer sequence of same functionality, in case the branc target is out of reach for the instruction's displacement. For example, the replacement sequence for bne would be
beq skip jmp target skip:In case there is no fitting 'opposite' for an instruction, the sequence may become even longer, e.g. for jbc:
jbc dobr bra skip dobr: jmp target skip:This feature however has the side effect that there is no unambigious assignment between machine and assembly code any more. Furthermore, additional passes may be the result if there are forward branches. One should therefore use this feature with caution!
G"ultigkeit: 8080/8085
With ON as argument, one can optionally write (almost) all 8080 instructions in the form Zilog defined them for the Z80. For instance, you simply use LD with self-explaining operands instead of MVI, LXI, MOV, STA, LDA, SHLD, LHLD, LDAX, STAX or SPHL.
Since some mnemonics have a different meaning in 8080 and Z80 syntax, it is not possible to program in 'Z80 style' all the time. The limits of this operation mode can be looked up in detail in section 4.19.
This pair of instructions may be used to frame a piece of code that is expected to trigger one or more error or warning messages. If the errors or warnings (identified by their numbers, see chapter A) do occur, they are suppressed and assembly continues without any error (naturally, without creating code at the erroneous places). However, if warnings or errors that were expected do not occur, ENDEXPECT will emit errors about them. The main usage scenario of these instructions are the self tests in the tests/ subdirectory. For instance, one may check this way if range checking of operands works as expected:
cpu 68000 expect 1320 ; immediate shift only for 1..8 lsl.l #10,d0 endexpect
The instructions described in this section partially overlap in their functionality, but each processor family defines other names for the same function. To stay compatible with the standard assemblers, this way of implementation was chosen.
If not explicitly mentioned otherwise, all instructions for data deposition (not those for reservation of memory!) allow an arbitrary number of parameters which are being processed from left to right.
valid for: 680x0, M*Core, 68xx, H8, SH7x00, DSP56xxx, XA, ST7/STM8, MN161x
This instruction places one or several constants of the type specified by the attribute into memory. The attributes are the same ones as defined in section 2.5, and there is additionally the possibility for byte constants to place string constants in memory, like
String dc.B "Hello world!\0"The parameter count may be between 1 and 20. A repeat count enclosed in brackets may additionally be prefixed to each parameter; for example, one can for example fill the area up to the next page boundary with zeroes with a statement like
dc.b [(*+255)&$ffffff00-*]0CAUTION! This function easily allows to reach the limit of 1 Kbyte of generated code per line!
The assembler can automatically add another byte of data in case the byte sum should become odd, to keep the word alignment. This behaviour may be turned on and off via the PADDING instruction.
Decimal floating point numbers stored with this instruction (DC.P...) can cover the whole range of extended precision, one however has to pay attention to the detail that the coprocessors currently available from Motorola (68881/68882) ignore the thousands digit of the exponent at the read of such constants!
The default attribute is W, that means 16-bit-integer numbers.
For the DSP56xxx, the data type is fixed to integer numbers (an attribute is therefore neither necessary nor allowed), which may be in the range of -8M up to 16M-1. String constants are also allowed, whereby three characters are packed into each word.
Opposed to the standar Motorola ssembler, it is also valid to reserve memory space with this statement, by using a question mark as operand. This is an extension added by some third-party suppliers for 68K assemblers, similar to what Intel assemblers provide. However, it should be clear that usage of this feature may lead to portability problems. Furthermore, question marks as operands must not be mixed with 'normal' constants in a single statement.
valid for: 680x0, M*Core, 68xx, H8, SH7x00, DSP56xxx, XA, ST7/STM8, MN161x
On the one hand, this instruction enables to reserve memory space for the specified count of numbers of the type given by the attribute. Therefore,
DS.B 20for example reserves 20 bytes of memory, but
DS.X 20reserves 240 bytes!
The other purpose is the alignment of the program counter which is achieved by a count specification of 0. In this way, with a
DS.W 0 ,the program counter will be rounded up to the next even address, with a
DS.D 0in contrast to the next double word boundary. Memory cells possibly staying unused thereby are neither zeroed nor filled with NOPs, they simply stay undefined.
The default for the operand length is - as usual - W, i.e. 16 bits.
For the 56xxx, the operand length is fixed to words (of 24 bit), attributes therefore do not exist just as in the case of DC.
valid for: Intel (except for 4004/4040), Zilog, Toshiba, NEC, TMS370, Siemens, AMD, MELPS7700/65816, M16(C), National, ST9, TMS70Cxx, TMS1000, Signetics, µPD77230, Fairchild, Intersil, XS1
These commands are - one could say - the Intel counterpart to DS and DC, and as expected, their logic is a little bit different: First, the specification of the operand length is moved into the mnemonic:
db ? ; reserves a byte dw ?,? ; reserves memory for 2 words (=4 byte) dd -1 ; places the constant -1 (FFFFFFFFH) !Reserved memory and constant definition must not be mixed within one instruction:
db "hello",? ; --> error messageAdditionally, the DUP Operator permits the repeated placing of constant sequences or the reservation of whole memory blocks:
db 3 dup (1,2) ; --> 1 2 1 2 1 2 dw 20 dup (?) ; reserves 40 bytes of memoryAs you can see, the DUP-argument must be enclosed in parentheses, which is also why it may consist of several components, that may themselves be DUPs...the stuff therefore works recursively. DUP is however also a place where one can get in touch with another limit of the assembler: a maximum of 1024 bytes of code or data may be generated in one line. This is not valid for the reservation of memory, only for the definition of constant arrays!
In order to be compatible to the M80, DEFB/DEFW may be used instead of DB/DW in Z80-mode.
Similarly, BYTE/ADDR resp. WORD/ADDRW in COP4/8 mode are an alias for DB resp. DW, with the pairs differing in byte order: instructions defined by National for address storage use big endian, BYTE resp. WORD in contrast use little endian.
The NEC 77230 is special with its DW instruction: It more works like the DATA statement of its smaller brothers, but apart from string and integer arguments, it also accepts floating point values (and stores them in the processor's proprietary 32-bit format). There is no DUP operator!
valid for: Intel, Zilog, Toshiba, NEC, TMS370, Siemens, AMD, M16(C), National, ST9, TMS7000, TMS1000, Intersil
With this instruction, you can reserve a memory area:
DS <count>It is an abbreviation of
DB <count> DUP (?)Although this could easily be made by a macro, some people grown up with Motorola CPUs (Hi Michael!) suggest DS to be a built-in instruction...I hope they are satisfied now ;-)
DS8 is defined as an alias for DS on the National SC14xxx. Beware that the code memory of these processors is organized in words of 16 bits, it is therefore impossible to reserve individual bytes. In case the argument of DS is odd, it will be rounded up to the next even number.
valid for: 6502, 68xx
By this instruction, byte constants or ASCII strings are placed in 65xx/68xx-mode, it therefore corresponds to DC.B on the 68000 or DB on Intel. Similarly to DC, a repetition factor enclosed in brackets ([..]) may be prepended to every single parameter.
valid for: ST6, 320C2(0)x, 320C5x, MSP, TMS9900
Ditto. Note that when in 320C2(0)x/5x mode, the assembler assumes that a label on the left side of this instruction has no type, i.e. it belongs to no address space. This behaviour is explained in the processor-specific hints.
The PADDING instruction allows to set whether odd counts of bytes shall be padded with a zero byte in MSP/TMS9900 mode.
valid for: SC144xx
This statement is an alias for DB, i.e. it may be used to dump byte constants or strings to memory.
valid for: 6502, 68xx
ADR resp. FDB stores word constants when in 65xx/68xx mode. It is therefore the equivalent to DC.W on the 68000 or DW on Intel platforms. Similarly to DC, a repetition factor enclosed in brackets ([..]) may be prepended to every single parameter.
valid for: ST6, i960, 320C2(0)x, 320C3x/C4x/C5x, MSP
If assembling for the 320C3x/C4x or i960, this command stores 32-bit words, 16-bit words for the other families. Note that when in 320C2(0)x/5x mode, the assembler assumes that a label on the left side of this instruction has no type, i.e. it belongs to no address space. This behaviour is explained at the discussion on processor-specific hints.
valid for: SC144xx
This instruction is for SC144xx targets a way to dump word (16 bit) constants to memory. CAUTION!! It is therefore an alias for DW.
valid for: 320C2(0)x, 320C5x
LONG stores a 32-bit integer to memory with the order LoWord-HiWord. Note that when in 320C2(0)x/5x mode, the assembler assumes that a label on the left side of this instruction has no type, i.e. it belongs to no address space. This behaviour is explained in the processor-specific hints.
valid for: 320C3x/C4x (not DOUBLE), 320C6x (not EXTENDED)
Both commands store floating-point constants to memory. In case of the 320C3x/C4x, they are not stored in IEEE-format. Instead the processor-specific formats with 32 and 40 bit are used. In case of EXTENDED the resulting constant occupies two memory words. The most significant 8 bits (the exponent) are written to the first word while the other ones (the mantissa) are copied into the second word.
valid for: 320C2(0)x, 320C5x
These two commands store floating-point constants in memory using the standard IEEE 32-bit and 64-bit IEEE formats. The least significant byte is copied to the first allocated memory location. Note that when in 320C2(0)x/5x mode the assembler assumes that all labels on the left side of an instruction have no type, i.e. they belong to no address space. This behaviour is explained in the processor-specific hints.
valid for: 320C2(0)x, 320C5x
Another three floating point commands. All of them support non-IEEE formats, which should be easily applicable on signal processors:
valid for: 320C2(0)x, 320C5x
Qxx and LQxx can be used to generate constants in a fixed point format. xx denotes a 2-digit number. The operand is first multiplied by 2 xx before converting it to binary notation. Thus xx can be viewed as the number of bits which should be reserved for the fractional part of the constant in fixed point format. Qxx stores only one word (16 bit) while LQxx stores two words (low word first):
q05 2.5 ; --> 0050h lq20 ConstPI ; --> 43F7h 0032hPlease do not flame me in case I calculated something wrong on my HP28...
valid for: PIC, 320xx, AVR, MELPS-4500, HMCS400,
4004/4040, µPD772x, OLMS-40/50
This command stores data in the current segment. Both integer values as well as character strings are supported. On 16C5x/16C8x, 17C4x in data segment, and on the 4500, 4004, and HMCS400 in code segment, characters occupy one word. On AVR, 17C4x in code segment, µPD772x in the data segments, and on 3201x/3202x, in general two characters fit into one word (LSB first). The µPD77C25 can hold three bytees per word in the code segment. When in 320C3x/C4x, mode the assembler puts four characters into one word (MSB first). In contrast to this characters occupy two memory locations in the data segment of the 4500, similar in the 4004 and HMCS400. The range of integer values corresponds to the word width of each processor in a specific segment. This means that DATA has the same result than WORD on a 320C3x/C4x (and that of SINGLE if AS recognizes the operand as a floating-point constant).
valid for: PIC
Generates a continuous string of zero words in memory.
valid for: COP4/8
These instruction allow to fill memory blocks with a byte or word constant. The first operand specifies the size of the memory block while the second one sets the filling constant itself.
valid for: ST6
Both commands store string constants to memory. While ASCII writes the character information only, ASCIZ additionally appends a zero to the end of the string.
valid for: 320C2(0)x, 320C5x
These commands are functionally equivalent to DATA, but integer values are limited to the range of byte values. This enables two characters or numbers to be packed together into one word. Both commands only differ in the order they use to write bytes: STRING stores the upper one first then the lower one, RSTRING does this vice versa. Note that when in 320C2(0)x/5x mode the assembler assumes that a label on the left side of this instruction has no type, i.e. it belongs to no address space. This behaviour is explained in the processor-specific hints.
valid for: 6502, 68xx
When in 65xx/68xx mode, string constants are generated using this instruction. In contrast to the original assembler AS11 from Motorola (this is the main reason why AS understands this command, the functionality is contained within the BYT instruction) you must enclose the string argument by double quotation marks instead of single quotation marks or slashes. Similarly to DC, a repetition factor enclosed in brackets ([..]) may be prepended to every single parameter.
valid for: 6502, 68xx
Reserves a memory block when in 6502/68xx mode. It is therefore the equivalent to DS.B on the 68000 or DB ? on Intel platforms.
valid for: ST6
Ditto.
valid for: i960
Ditto.
valid for: PIC, MELPS-4500, HMCS400, 3201x, 320C2(0)x, 320C5x, AVR, µPD772x, OLMS-40/50
This command allocates memory. When used in code segments the argument counts words (10/12/14/16 bit). In data segments it counts bytes for PICs, nibbles for 4500 and OLMS-40/50 and words for the TI devices.
valid for: 320C2(0)x, 320C3x/C4x/C5x/C6x, MSP
BSS works like RES, but when in 320C2(0)x/5x mode, the assembler assumes that a label on the left side of this instruction has no type, i.e it belongs to no address space. This behaviour is explained in the processor-specific hints.
valid for: COP4/8
Both instructions allocate memory and ensure compatibility to ASMCOP from National. While DSB takes the argument as byte count, DSW uses it as word count (thus it allocates twice as much memory than DSB).
valid for: SC144xx
This instruction reserves memory in steps of full words, i.e. 16 bits. It is an alias for DW.
valid for: all processors
Takes the argument to align the program counter to a certain address boundary. AS increments the program counter to the next multiple of the argument. So, ALIGN corresponds to DS.x on 68000, but is much more flexible at the same time.
Example:
align 2aligns to an even address (PC mod 2 = 0). If Align is used in this form with only one argument, the contents of the skipped memory space is not defined. An optinal second argument may be used to define the (byte) value used to fill the area.
valid for: SH7x00
Although the SH7000 processor can do an immediate register load with 8 bit only, AS shows up with no such restriction. This behaviour is instead simulated through constants in memory. Storing them in the code segment (not far away from the register load instruction) would require an additional jump. AS Therefore gathers the constants an stores them at an address specified by LTORG. Details are explained in the processor-specific section somewhat later.
valid for: all processors
Now we finally reach the things that make a macro assembler different from an ordinary assembler: the ability to define macros (guessed it !?).
When speaking about 'macros', I generally mean a sequence of (machine or pseudo) instructions which are united to a block by special statements and can then be treated in certain ways. The assembler knows the following statements to work with such blocks:
is probably the most important instruction for macro programming. The instruction sequence
<name> MACRO [parameter list] <instructions> ENDMdefines the macro <name> to be the enclosed instruction sequence. This definition by itself does not generate any code! In turn, from now on the instruction sequence can simply be called by the name, the whole construct therefore shortens and simplifies programs. A parameter list may be added to the macro definition to make things even more useful. The parameters' names have to be separated by commas (as usual) and have to conform to the conventions for symbol names (see section 2.7) - like the macro name itself.
A switch to case-sensitive mode influences both macro names and parameters.
Similar to symbols, macros are local, i.e. they are only known in a section and its subsections when the definition is done from within a section. This behaviour however can be controlled in wide limits via the options PUBLIC and GLOBAL described below.
A default value may be provided for each macro parameter (appended via an equal sign). This value is used if there is no argument for this parameter at macro call or if the positional argument (see below) for this parameter is empty.
Apart from the macro parameters themselves, the parameter list may contain control parameters which influence the processing of the macro. These parameters are distinguished from normal parameters by being enclosed in braces. The following control parameters are defined:
When a macro is called, the parameters given for the call are textually inserted into the instruction block and the resulting assembler code is assembled as usual. Zero length parameters are inserted in case too few parameters are specified. It is important to note that string constants are not protected from macro expansions. The old IBM rule:
It's not a bug, it's a feature!applies for this detail. The gap was left to allow checking of parameters via string comparisons. For example, one can analyze a macro parameter in the following way:
mul MACRO para,parb IF UpString("PARA")<>"A" MOV a,para ENDIF IF UpString("PARB")<>"B" MOV b,parb ENDIF !mul ab ENDMIt is important for the example above that the assembler converts all parameter names to upper case when operating in case-insensitive mode, but this conversion never takes place inside of string constants. Macro parameter names therefore have to be written in upper case when they appear in string constants.
Macro arguments may be given in either of two forms: positional or keyword arguments.
For positional arguments, the assignment of arguments to macro parameters simply results from the position of arguments, i.e. the first argument is assigned to the first parameter, the second argument to the second parameter and so on. If the number of arguments is smaller than the number of parameters, eventually defined default values or simply an empty string are inserted. The same is valid for empty arguments in the argument list.
Keyword arguments on the other hand explicitly define which parameter they relate to, by being prefixed with the parameter's name:
mul para=r0,parb=r1Again, non-assigned parameters will use an eventually defined default or an empty string.
As a difference to positional arguments, keyword arguments allow to assign an empty string to a parameter with a non-empty default.
Mixing of positional and keyword arguments in one macro call is possible, however it is not allowed to use positional arguments after the first keyword argument.
The same naming rules as for usual symbols also apply for macro parameters, with the exception that only letters and numbers are allowed, i.e. dots and underscores are forbidden. This constraint has its reason in a hidden feature: the underscore allows to concatenate macro parameter names to a symbol, like in the following example:
concat macro part1,part2 call part1_part2 endmThe call
concat module,functionwill therefore result in
call module_functionApart from the parameters explicitly declared for a macro, four more 'implicitly' declared parameters exist. Since they are always present, they cannot not be redeclared as explicit parameters:
The purpose of being able to 'internally' use a label in a macro is surely not immediately obvious. There might be cases where moving the macro's entry point into its body may be useful. The most important application however are TI signal processors that use a double pipe symbol in the label's column to mark parallelism, like this:
instr1 || instr2(since both instructions merge into a single word of machine code, you cannot branch to the second instruction - so occupying the label's position doesn't hurt). The problem is however that some 'convenience instructions' are realized as macros. A prallelization symbol written in front of a macro call normally would be assigned to the macro itself, not to the macro body's first instruction. However, things work with this trick:
myinstr macro {INTLABEL} __LABEL__ instr2 endm instr1 || myinstrThe result after expanding myinstr is identical to the previous example without macro.
Recursion of macros, i.e. the repeated call of a macro from within its own body is completely legal. However, like for any other sort of recursion, one has to assure that there is an end at someplace. For cases where one forgot this, AS keeps an internal counter for every macro that is incremented when an expansion of this macro is begun and decremented again when the expansion is completed. In case of recursive calls, this counter reaches higher and higher values, and at a limit settable via NESTMAX, AS will refuse to expand. Be careful when you turn off this emergency brake: the memory consumption on the heap may go beyond all limits and even shut down a Unix system...
A small example to remove all clarities ;-)
A programmer braindamaged by years of programming Intel processors wants to have the instructions PUSH/POP also for the 68000. He solves the 'problem' in the following way:
push macro op move.ATTRIBUTE op,-(sp) endm pop macro op move.ATTRIBUTE (sp)+,op endmIf one writes
push d0 pop.l a2 ,this results in
move. d0,-(sp) move.l (sp)+,a2A macro definition must not cross include file boundaries.
Labels defined in macros always are regarded as being local, unless the GLOBALSYMBOLS was used in the macro's definition. If a single label shall be made public in a macro that uses local labels otherwise, it may be defined with a LABEL statement which always creates global symbols (similar to BIT, SFR...):
<Name> label $When parsing a line, the assembler first checks the macro list afterwards looks for processor instructions, which is why macros allow to redefine processor instructions. However, the definition should appear previously to the first invocation of the instruction to avoid phase errors like in the following example:
bsr target bsr macro targ jsr targ endm bsr targetIn the first pass, the macro is not known when the first BSR instruction is assembled; an instruction with 4 bytes of length is generated. In the second pass however, the macro definition is immediately available (from the first pass), a JSR of 6 bytes length is therefore generated. As a result, all labels following are too low by 2 and phase errors occur for them. An additional pass is necessary to resolve this.
Because a machine or pseudo instruction becomes hidden when a macro of same name is defined, there is a backdoor to reach the original meaning: the search for macros is suppressed if the name is prefixed with an exclamation mark (!). This may come in handy if one wants to extend existing instructions in their functionality, e.g. the TLCS-90's shift instructions:
srl macro op,n ; shift by n places rept n ; n simple instructions !srl op endm endmFrom now on, the SRL instruction has an additional parameter...
If a macro is being called, the macro's body is included in the assembly listing, after arguemnts have been expanded. This can significantly increase the listing's size and make it hard to read. It is therefore possible to suppress this expansion totally or in parts. Fundamentally, AS divides the source lines contained in a macro's body into three classes:
In consequence, changing the set via MACEXP_DFT has no effect on macros that have been defined before this statement. The listing's section shows for defined macros the effective set of expansion directives. The list given in curly braces is shorted so that it only conatins the last (and therefore valid) directive for a certain class of source lines. A NOIF given via MACEXP_DFT will therefore not show up if the directive EXPIF had been given specifically for this macro.
There might be cases where it is useful to override the expansion rules for a certain macro, regardless whether they were given by MACEXP_DFT or individual directives. The statement MACEXP_OVR (3.7.3) exists for such cases. It only has an effects on macros subsequently being expanded. Once again, directives given by this instruction are regarded in addition to a macro's rules, and they do with higher priority. A MACEXP_OVR without any arguments disables such an override.
is a simplified macro definition for the case that an instruction sequence shall be applied to a couple of operands and the the code is not needed any more afterwards. IRP needs a symbol for the operand as its first parameter, and an (almost) arbitrary number of parameters that are sequentially inserted into the block of code. For example, one can write
irp op, acc,b,dpl,dph push op endmto push a couple of registers to the stack, what results in
push acc push b push dpl push dphAnalog to a macro definition, the argument list may contain the control parameters GLOBALSYMBOLS resp. NOGLOBALSYMBOLS (marked as such by being enclosed in curly braces). This allows to control whether used labels are local for every pass or not.
IRPC is a variant of IRP where the first argument's occurences in the lines up to ENDM are successively replaced by the characters of a string instead of further parameters. For example, an especially complicated way of placing a string into memory would be:
irpc char,"Hello World" db 'CHAR' endmCAUTION! As the example already shows, IRPC only inserts the pure character; it is the programmer's task to assure that valid code results (in this example by inserting quotes, including the detail that no automatic conversion to uppercase characters is done).
is the simplest way to employ macro constructs. The code between REPT and ENDM is assembled as often as the integer argument of REPT specifies. This statement is commonly used in small loops to replace a programmed loop to save the loop overhead.
An example for the sake of completeness:
rept 3 rr a endmrotates the accumulator to the right by three digits.
The optional control parameters GLOBALSYMBOLS resp. NOGLOBALSYMBOLS (marked as such by being enclosed in curly braces) may also be used here to decide whether labels are local to the individual repetitions.
In case REPT's argument is equal to or smaller than 0, no expansion at all is done. This is different to older versions of AS which used to be a bit 'sloppy' in this respect and always made a single expansion.
WHILE operates similarly to REPT, but the fixed number of repetitions given as an argument is replaced by a boolean expression. The code framed by WHILE and ENDM is assembled until the expression becomes logically false. This may mean in the extreme case that the enclosed code is not assembled at all in case the expression was already false when the construct was found. On the other hand, it may happen that the expression stays true forever and AS will run infinitely...one should apply therefore a bit of accuracy when one uses this construct, i.e. the code must contain a statement that influences the condition, e.g. like this:
cnt set 1 sq set cnt*cnt while sq<=1000 dc.l sq cnt set cnt+1 sq set cnt*cnt endmThis example stores all square numbers up to 1000 to memory.
Currently there exists a little ugly detail for WHILE: an additional empty line that was not present in the code itself is added after the last expansion. This is a 'side effect' based on a weakness of the macro processor and it is unfortunately not that easy to fix. I hope noone minds...
EXITM offers a way to terminate a macro expansion or one of the instructions REPT, IRP, or WHILE prematurely. Such an option helps for example to replace encapsulations with IF-ENDIF-ladders in macros by something more readable. Of course, an EXITM itself always has to be conditional, what leads us to an important detail: When an EXITM is executed, the stack of open IF and SWITCH constructs is reset to the state it had just before the macro expansion started. This is imperative for conditional EXITM's as the ENDIF resp. ENDCASE that frames the EXITM statement will not be reached any more; AS would print an error message without this trick. Please keep also in mind that EXITM always only terminates the innermost construct if macro constructs are nested! If one want to completely break out of a nested construct, one has to use additional EXITM's on the higher levels!
SHIFT is a tool to construct macros with variable argument lists: it discards the first parameter, with the result that the second parameter takes its place and so on. This way one could process a variable argument list...if you do it the right way. For example, the following does not work...
pushlist macro reg rept ARGCOUNT push reg shift endm endm...because the macro gets expanded once, its output is captured by REPT and then executed n times. Therefore, the first argument is saved n times...the following approach works better:
pushlist macro reg if "REG"<>"" push reg shift pushlist ALLARGS endif endmEffectively, this is a recursion that shortens the argument list once per step. The important trick is that a new macro expansion is started in each step...
MAXNEST allows to adjust how often a mcro may be called recursively before AS terminates with an error message. The argument may be an arbitrary positive integer value, with the special value 0 turning the this security brake completely off (be careful with that...). The default value for the maximum nesting level is 256; its current value may be read from the integer symbol of same name.
Though FUNCTION is not a macro statement in the inner sense, I will describe this instruction at this place because it uses similar principles like macro replacements.
This instruction is used to define new functions that may then be used in formula expressions like predefined functions. The definition must have the following form:
<name> FUNCTION <arg>,..,<arg>,<expression>The arguments are the values that are 'fed into' the function. The definition uses symbolic names for the arguments. The assembler knows by this that where to insert the actual values when the function is called. This can be seen from the following example:
isdigit FUNCTION ch,(ch>='0')&&(ch<='9')This function checks whether the argument (interpreted as a character) is a number in the currently valid character set (the character set can be modified via CHARSET, therefore the careful wording).
The arguments' names (CH in this case) must conform to the stricter rules for macro parameter names, i.e. the special characters . and _ are not allowed.
User-defined functions can be used in the same way as builtin functions, i.e. with a list of parameters, separated by commas, enclosed in parentheses:
IF isdigit(char) message "\{char} is a number" ELSEIF message "\{char} is not a number" ENDIFWhen the function is called, all parameters are calculated once and are then inserted into the function's formula. This is done to reduce calculation overhead and to avoid side effects. The individual arguments have to be separated by commas when a function has more than one parameter.
CAUTION! Similar to macros, one can use user-defined functions to override builtin functions. This is a possible source for phase errors. Such definitions therefore should be done before the first call!
The result's type may depend on the type of the input arguments as the arguments are textually inserted into the function's formula. For example, the function
double function x,x+xmay have an integer, a float, or even a string as result, depending on the argument's type!
When AS operates in case-sensitive mode, the case matters when defining or referencing user-defined functions, in contrast to builtin functions!
valid for: all processors
Even in assembly language programs, there is sometimes the necessity to define composed data structures, similar to high-level languages. AS supports both the definition and usage of structures with a couple of statements. These statements shall be explained in the following section.
The definiton of a structure is begun with the statement STRUCT and ends with ENDSTRUCT (lazy people may also write STRUC resp. ENDSTRUC or ENDS instead). A optional label preceding these instructions is taken as the name of the structure to be defined; it is optional at the end of the definition and may be used to redefine the length symbol's name (see below). The remaining procedure is simple: Together with STRUCT, the cuurent program counter is saved and reset to zero. All labels defined between STRUCT and ENDSTRUCT therefore are the offsets of the structure's data fields. Reserving space is done via the same instructions that are also otherwise used for reserving space, like e.g. DS.x for Motorola CPUs or DB & co. for Intel-style processors. The rules for rounding up lengths to assure certain alignments also apply here - if one wants to define 'packed' structures, a preceding PADDING OFF may be necessary. Vice versa, alignments may be forced with ALIGN or similar instructions.
Since such a definition only represents a sort of 'prototype', only instructions that reserve memory may be used, no instructions that dispose constants or generate code.
Labels defined inside structures (i.e. the elements' names) are not stored as-is. Instead, the structure's name is prepended to them, separated with a special character. By default, this is the underbar (_). This behaviour however may be modified with two arguments passed to the STRUCT statement:
dottedstructs <on|off>
Aside from the element names, AS also defines a further symbol with the structure's overall length when the definition has been finished. This symbol has the name LEN, which is being extended with the structure's name via the same rules - or alternitavely with the label name given with the ENDSTRUCT statement.
In practice, this may things may look like in this example:
Rec STRUCT Ident db ? Pad db ? Pointer dd ? Rec ENDSTRUCTIn this example, the symbol REC_LEN would be assigned the value 6.
Once a structure has been assigned, usage is as simple as possible and similar to a macro: a simple
thisrec Recreserves as much memory as needed to hold an instance of the structure, and additionally defines a symbol for every element of the structure with its address, in this case THISREC_IDENT, THISREC_PAD, and THISREC_POINTER. A label naturally must not be omitted when calling a structure; if it is missing, an error will be emitted.
ATTENTION! Though AS does not expect any arguments when calling a structure, any arguments given will simply be ignored and not be reported as error. This is a precaution to allow the definition of pre-initialized structures in the future.
Is is perfectly valid to call an already defined structure within the definition of another structure. The procedure that is taking place then is a combination of the definition and calling described in the previous two sections: elements of the substructure are being defined, the name of the instance is being prepended, and the name of the super-structure is once again geing prepended to this concatenated name. This may look like the following:
TreeRec struct left dd ? right dd ? data Rec TreeRec endstruct
It is also allowed to define one structure inside of another structure:
TreeRec struct left dd ? right dd ? TreeData struct name db 32 dup(?) id dw ? TreeData endstruct TreeRec endstruct
A union is a special form of a structure whose elements are not laid out sequentially in memory. Instead all elements occupy the same memory and are located at offset 0 in the structure. Naturally, suich a defnition basically does nothing more than to assign the value of zero to a couple of symbols. It may however be useful to clarify the overlap in a program and therefore to make it more 'readable'. The size of a union is the maximum of all elements' lengths.
The name of a structure or union is optional if it is part of another (named) structure or union. Elements of this structure will then become part of of the 'next higher' named structure. For example,
TreeRec struct left dd ? right dd ? struct name db 32 dup(?) id dw ? endstruct TreeRec endstructgenerates the symbols TREEREC_NAME and TREEREC_ID.
Futhermore, no symbol holding its length is generated for an unnamed structure or union.
Symbols that are created in the course of defining or usage of structures are treated just like normal symbols, i.e. when used within a section, these symbols are local to the section. The same is however also true for the structures themselves, i.e. a structure defined within a section cannot be used outside of the section.
If one wants to instantiate structures via macros, one has to use the GLOBALSYMBOLS options when defining the macro to make the defined symbols visible outside the macro. For instance, a list of structures can be defined in the following way:
irp name,{GLOBALSYMBOLS},rec1,rec2,rec3 name Rec endm
valid for: all processors
The assembler supports conditional assembly with the help of statements like IF... resp. SWITCH... . These statements work at assembly time allowing or disallowing the assembly of program parts based on conditions. They are therefore not to be compared with IF statements of high-level languages (though it would be tempting to extend assembly language with structurization statements of higher level languages...).
The following constructs may be nested arbitrarily (until a memory overflow occurs).
IF is the most common and most versatile construct. The general style of an IF statement is as follows:
IF <expression 1> . . <block 1> . . ELSEIF <expression 2> . . <block 2> . . (possibly more ELSEIFs) . . ELSEIF . . <block n> . . ENDIFIF serves as an entry, evaluates the first expression, and assembles block 1 if the expression is true (i.e. not 0). All further ELSEIF-blocks will then be skipped. However, if the expression is false, block 1 will be skipped and expression 2 is evaluated. If this expression turns out to be true, block 2 is assembled. The number of ELSEIF parts is variable and results in an IF-THEN-ELSE ladder of an arbitrary length. The block assigned to the last ELSEIF (without argument) only gets assembled if all previous expressions evaluated to false; it therefore forms a 'default' branch. It is important to note that only one of the blocks will be assembled: the first one whose IF/ELSEIF had a true expression as argument.
The ELSEIF parts are optional, i.e. IF may directly be followed by an ENDIF. An ELSEIF without parameters must be the last branch.
ELSEIF always refers to the innermost, unfinished IF construct in case IF's are nested.
In addition to IF, the following further conditional statements are defined:
It is valid to write ELSE instead of ELSEIF since everybody seems to be used to it...
For everyIF... statement, there has to be a corresponding ENDIF. 'Open' constructs will lead to an error message at the end of an assembly path. The way AS has 'paired' ENDIF statements with IFs may be deduced from the assembly listing: for ENDIF, the line number of the corresponding IF... will be shown.
CASE is a special case of IF and is designed for situations when an expression has to be compared with a couple of values. This could of course also be done with a series of ELSEIFs, but the following form
SWITCH <expression> . . CASE <value 1> . <block 1> . CASE <value 2> . <block 2> . (further CASE blocks) . CASE <value n-1> . <block n-1> . ELSECASE . <block n> . ENDCASEhas the advantage that the expression is only written once and also only gets evaluated once. It is therefore less error-prone and slightly faster than an IF chain, but obviously not as flexible.
It is possible to specify multiple values separated by commas to a CASE statement in order to assemble the following block in multiple cases. The ELSECASE branch again serves as a 'trap' for the case that none of the CASE conditions was met. AS will issue a warning in case it is missing and all comparisons fail.
Even when value lists of CASE branches overlap, only one branch is executed, which is the first one in case of ambiguities.
SWITCH only serves to open the whole construct; an arbitrary number of statements may be between SWITCH and the first CASE (but don't leave other IFs open!), for the sake of better readability this should however not be done.
In case that SWITCH is already a machine instruction on the selected processor target, the construct is started instead with SELECT.
Similarly to IF constructs, there must be exactly one ENDCASE for every SWITCH. Analogous to ENDIF, for ENDCASE the line number of the corresponding SWITCH is shown in the listing.
valid for: all processors
PAGE is used to tell AS the dimensions of the paper that is used to print the assembly listing. The first parameter is thereby the number of lines after which AS shall automatically output a form feed. One should however take into account that this value does not include heading lines including an eventual line specified with TITLE. The minimum number of lines is 5, and the maximum value is 255. A specification of 0 has the result that AS will not do any form feeds except those triggered by a NEWPAGE instruction or those implicitly engaged at the end of the assembly listing (e.g. prior to the symbol table).
The specification of the listing's length in characters is an optional second parameter and serves two purposes: on the one hand, the internal line counter of AS will continue to run correctly when a source line has to be split into several listing lines, and on the other hand there are printers (like some laser printers) that do not automatically wrap into a new line at line end but instead simply discard the rest. For this reason, AS does line breaks by itself, i.e. lines that are too long are split into chunks whose lengths are equal to or smaller than the specified width. This may lead to double line feeds on printers that can do line wraps on their own if one specifies the exact line width as listing width. The solution for such a case is to reduce the assembly listing's width by 1. The specified line width may lie between 5 and 255 characters; a line width of 0 means similarly to the page length that AS shall not do any splitting of listing lines; lines that are too long of course cannot be taken into account of the form feed then any more.
The default setting for the page length is 60 lines, the default for the line width is 0; the latter value is also assumed when PAGE is called with only one parameter.
In case PAGE is already a machine instruction on the selected processor target, use instead PAGESIZE to define the paper size.
CAUTION! There is no way for AS to check whether the specified listing length and width correspond to the reality!
NEWPAGE can be used to force a line feed though the current line is not full up to now. This might be useful to separate program parts in the listing that are logically different. The internal line counter is reset and the page counter is incremented by one. The optional parameter is in conjunction with a hierarchical page numbering AS supports up to a chapter depth of 4. 0 always refers to the lowest depth, and the maximum value may vary during the assembly run. This may look a bit puzzling, as the following example shows:
NEWPAGE <number> may therefore result in changes in different digits, depending on the current chapter depth. An automatic form feed due to a line counter overflow or a NEWPAGE without parameter is equal to NEWPAGE 0. Previous to the output of the symbol table, an implicit NEWPAGE <maximum up to now> is done to start a new 'main chapter'.
page 1, instruction NEWPAGE 0 -> page 2 page 2, instruction NEWPAGE 1 -> page 2.1 page 2.1, instruction NEWPAGE 1 -> page 3.1 page 3.1, instruction NEWPAGE 0 -> page 3.2 page 3.2, instruction NEWPAGE 2 -> page 4.1.1
Once a macro is tested and 'done', one might not want to see it in the listing when it is used. As described in the section about defining and using macros (3.4.1), additional arguments to the MACRO statement allow to control whether a macro's body is expanded upon its usage and if yes, which parts of it. In case that several macros are defined in a row, it is not necessary to give these directives for every single macro. The pseudo instruction MACEXP_DFT defines for all following macros which parts shall be expanded upon invocation of the macro:
MACEXP_DFT noif,nomacrohas the result that for macros defined in succession, only code parts that are no macro definition and that are not excluded via conditional assembly will be listed.
This instruction plus the per-macro directives provide fine-grained per-macro over the parts being expanded. However, there may be cases in practice where one wants to see the expanded code of a macro at one place and not at the other. This is possible by using the statement MACEXP_OVR: it accepts the same arguemnts like MACEXP_DFT, these however act as overrides for all macros being expanded in the following code. This is in contrast to MACEXP_DFT which influences macros being defined in the following code. For instance, if one defined for a macro that neither macro definitions nor conditional assembly shall be expanded in the listing, a
MACEXP_OVR MACROre-enables expansion of macro definitions for its following usages, while a
MACEXP_OVR ONforces expansion of the complete macro body in the listing. MACEXP_OVR without arguments again disables all overrides, macros will again behave as individually specified upon definition.
Both statements also have an effect on other macro-like constructs (REPT, IRP, IRPC WHILE). However, since these are expanded only one and ,,in-place'', the functional difference of these two statements becomes minimal. In case of differences, the override set via MACEXP_OVR has a higher priority.
The Setting currently made via MACEXP_DFT may be read from the predefined symbol MACEXP. For backward compatibility reasons, it is possible to use the statement MACEXP instead of MACEXP_DFT. However, one should not make use of this in new programs.
works like MACEXP and accepts the same parameters, but is much more radical: After a
listing off ,nothing at all will be written to the listing. This directive makes sense for tested code parts or include files to avoid a paper consumption going beyond all bounds. CAUTION! If one forgets to issue the counterpart somewhere later, even the symbol table will not be written any more! In addition to ON and OFF, LISTING also accepts NOSKIPPED and PURECODE as arguments. Program parts that were not assembled due to conditional assembly will not be written to the listing when NOSKIPPED is set, while PURECODE - as the name indicates - even suppresses the IF directives themselves in the listing. These options are useful if one uses macros that act differently depending on parameters and one only wants to see the used parts in the listing.
The current setting may be read from the symbol LISTING (0=OFF, 1=ON, 2=NOSKIPPED, 3=PURECODE).
Quite often it makes sense to switch to another printing mode (like compressed printing) when the listing is sent to a printer and to deactivate this mode again at the end of the listing. The output of the needed control sequences can be automated with these instructions if one specifies the sequence that shall be sent to the output device prior to the listing with PRTINIT <string> and similarly the deinitialization string with PRTEXIT <string>. <string> has to be a string expression in both cases. The syntax rules for string constants allow to insert control characters into the string without too much tweaking.
When writing the listing, the assembler does not differentiate where the listing actually goes, i.e. printer control characters are sent to the screen without mercy!
Example:
For Epson printers, it makes sense to switch them to compressed printing because listings are so wide. The lines
prtinit "\15" prtexit "\18"assure that the compressed mode is turned on at the beginning of the listing and turned off afterwards.
The assembler normally adds a header line to each page of the listing that contains the source file's name, date, and time. This statement allows to extend the page header by an arbitrary additional line. The string that has to be specified is an arbitrary string expression.
Example:
For the Epson printer already mentioned above, a title line shall be written in wide mode, which makes it necessary to turn off the compressed mode before:
title "\18\14Wide Title\15"(Epson printers automatically turn off the wide mode at the end of a line.)
RADIX with a numerical argument between 2 and 36 sets the default numbering system for integer constants, i.e. the numbering system used if nothing else has been stated explicitly. The default is 10, and there are some possible pitfalls to keep in mind which are described in section 2.10.1.
Independent of the current setting, the argument of RADIX is always decimal; furthermore, no symbolic or formula expressions may be used as argument. Only use simple constant numbers!
OUTRADIX can in a certain way be regarded as the opposite to RADIX: This statement allows to configure which numbering system to use for integer results when \{...} constructs are used in string constants (see section 2.10.3). Valid arguments range again from 2 to 36, while the default is 16.
valid for: all processors
local symbols and the section concept introduced with them are a completely new function that was introduced with version 1.39. One could say that this part is version ''1.0'' and therefore probably not the optimum. Ideas and (constructive) criticism are therefore especially wanted. I admittedly described the usage of sections how I imagined it. It is therefore possible that the reality is not entirely equal to the model in my head. I promise that in case of discrepancies, changes will occur that the reality gets adapted to the documentation and not vice versa (I was told that the latter sometimes takes place in larger companies...).
AS does not generate linkable code (and this will probably not change in the near future :-(). This fact forces one to always assemble a program in a whole. In contrast to this technique, a separation into linkable modules would have several advantages:
<subprogram name>_<symbol name>that had lengths ranging from 15 to 25 characters and made the program difficult to overlook. The concept of section described in detail in the following text was designed to cure at least the second and third item of the list above. It is completely optional: if you do not want to use sections, simply forget them and continue to work like you did with previous versions of AS.
A section represents a part of the assembler program enclosed by special statements and has a unique name chosen by the programmer:
. . <other code> . . SECTION <section's name> . . <code inside of the section> . . ENDSECTION [section's name] . . <other code> . .The name of a section must conform to the conventions for s symbol name; AS stores section and symbol names in separate tables which is the reason why a name may be used for a symbol and a section at the same time. Section names must be unique in a sense that there must not be more than one section on the same level with the same name (I will explain in the next part what ''levels'' mean). The argument of ENDSECTION is optional, it may also be omitted; if it is omitted, AS will show the section's name that has been closed with this ENDSECTION. Code inside a section will be processed by AS exactly as if it were outside, except for three decisive differences:
AS will by default not differentiate between upper and lower case in section names; if one however switches to case-sensitive mode, the case will be regarded just like for symbols.
The organization described up to now roughly corresponds to what is possible in the C language that places all functions on the same level. However, as my ''high-level'' ideal was Pascal and not C, I went one step further:
It is valid to define further sections within a section. This is analog to the option given in Pascal to define procedures inside a procedure or function. The following example shows this:
sym EQU 0 SECTION ModuleA SECTION ProcA1 sym EQU 5 ENDSECTION ProcA1 SECTION ProcA2 sym EQU 10 ENDSECTION ProcA2 ENDSECTION ModuleA SECTION ModuleB sym EQU 15 SECTION ProcB ENDSECTION ProcB ENDSECTION ModuleBWhen looking up a symbol, AS first searches for a symbol assigned to the current section, and afterwards traverses the list of parent sections until the global symbols are reached. In our example, the individual sections see the values given in table 3.6 for the symbol sym:
section | value | from section... |
---|---|---|
Global | 0 | Global |
ModuleA | 0 | Global |
ProcA1 | 5 | ProcA1 |
ProcA2 | 10 | ProcA2 |
ModuleB | 15 | ModuleB |
ProcB | 15 | ModuleB |
This rule can be overridden by explicitly appending a section's name to the symbol's name. The section's name has to be enclosed in brackets:
move.l #sym[ModulB],d0Only sections that are in the parent section path of the current section may be used. The special values PARENT0..PARENT9 are allowed to reference the n-th ''parent'' of the current section; PARENT0 is therefore equivalent to the current section itself, PARENT1 the direct parent and so on. PARENT1 may be abbreviated as PARENT. If no name is given between the brackets, like in this example:
move.l #sym[],d0 ,one reaches the global symbol. CAUTION! If one explicitly references a symbol from a certain section, AS will only seek for symbols from this section, i.e. the traversal of the parent sections path is omitted!
Similar to Pascal, it is allowed that different sections have subsections of the same name; the principle of locality avoids irritations. One should IMHO still use this feature as seldom as possible: Symbols listed in the symbol resp. cross reference list are only marked with the section they are assigned to, not with the ''section hierarchy'' lying above them (this really would have busted the available space); a differentiation is made very difficult this way.
As a SECTION instruction does not define a label by itself, the section concept has an important difference to Pascal's concept of nested procedures: a pascal procedure can automatically ''see'' its subprocedures(functions), AS requires an explicit definition of an entry point. This can be done e.g. with the following macro pair:
proc MACRO name SECTION name name LABEL $ ENDM endp MACRO name ENDSECTION name ENDMThis example also shows that the locality of labels inside macros is not influenced by sections. It makes the trick with the LABEL instruction necessary.
This does of course not solve the problem completely. The label is still local and not referencable from the outside. Those who think that it would suffice to place the label in front of the SECTION statement should be quiet because they would spoil the bridge to the next theme:
The PUBLIC statement allows to change the assignment of a symbol to a certain section. It is possible to treat multiple symbols with one statement, but I will use an example with only one symbol in the following (not hurting the generality of this discussion). In the simplest case, one declares a symbol to be global, i.e. it can be referenced from anywhere in the program:
PUBLIC <name>As a symbol cannot be moved in the symbol table once it has been sorted in, this statement has to appear before the symbol itself is defined. AS stores all PUBLICs in a list and removes an entry from this list when the corresponding symbol is defined. AS prints errors at the end of a section in case that not all PUBLICs have been resolved.
Regarding the hierarchical section concept, the method of defining a symbol as purely global looks extremely brute. There is fortunately a way to do this in a bit more differentiated way: by appending a section name:
PUBLIC <name>:<section>The symbol will be assigned to the referenced section and therefore also becomes accessible for all its subsections (except they define a symbol of the same name that hides the ''more global'' symbol). AS will naturally protest if several subsections try to export a symbol of same name to the same level. The special PARENTn values mentioned in the previous section are also valid for <section> to export a symbol exactly n levels up in the section hierarchy. Otherwise only sections that are parent sections of the current section are valid for <section>. Sections that are in another part of the section tree are not allowed. If several sections in the parent section path should have the same name (this is possible), the lowest level will be taken.
This tool lets the abovementioned macro become useful:
proc MACRO name SECTION name PUBLIC name:PARENT name LABEL $ ENDMThis setting is equal to the Pascal model that also only allows the ''father'' to see its children, but not the ''grandpa''.
AS will quarrel about double-defined symbols if more than one section attempts to export a symbol of a certain name to the same upper section. This is by itself a correct reaction, and one needs to ''qualify'' symbols somehow to make them distinguishable if these exports were deliberate. A GLOBAL statement does just this. The syntax of GLOBAL is identical to PUBLIC, but the symbol stays local instead of being assigned to a higher section. Instead, an additional symbol of the same value but with the subsection's name appended to the symbol's name is created, and only this symbol is made public according to the section specification. If for example two sections A and B both define a symbol named SYM and export it with a GLOBAL statement to their parent section, the symbols are sorted in under the names A_SYM resp. B_SYM .
In case that source and target section are separated by more than one level, the complete name path is prepended to the symbol name.
The model described so far may look beautiful, but there is an additional detail not present in Pascal that may spoil the happiness: Assembler allows forward references. Forward references may lead to situations where AS accesses a symbol from a higher section in the first pass. This is not a disaster by itself as long as the correct symbol is used in the second pass, but accidents of the following type may happen:
loop: . <code> . . SECTION sub . ; *** . bra.s loop . . loop: . . ENDSECTION . . jmp loop ; main loopAS will take the global label loop in the first pass and will quarrel about an out-of-branch situation if the program part at <code> is long enough. The second pass will not be started at all. One way to avoid the ambiguity would be to explicitly specify the symbol's section:
bra.s loop[sub]If a local symbol is referenced several times, the brackets can be saved by using a FORWARD statement. The symbol is thereby explicitly announced to be local, and AS will only look in the local symbol table part when this symbol is referenced. For our example, the statement
FORWARD loopshould be placed at the position marked with ***.
FORWARD must not only be stated prior to a symbol's definition, but also prior to its first usage in a section to make sense. It does not make sense to define a symbol private and public; this will be regarded as an error by AS.
The multi-stage lookup in the symbol table and the decision to which section a symbol shall be assigned of course cost a bit of time to compute. An 8086 program of 1800 lines length for example took 34.5 instead of 33 seconds after a modification to use sections (80386 SX, 16MHz, 3 passes). The overhead is therefore limited. As it has already been stated at the beginning, is is up to the programmer if (s)he wants to accept it. One can still use AS without sections.
valid for: all processors
This statement instructs AS to write the symbols given in the parameter list (regardless if they are integer, float or string symbols) together with their values into the share file. It depends upon the command line parameters described in section 2.4 whether such a file is generated at all and in which format it is written. If AS detects this instruction and no share file is generated, a warning is the result.
CAUTION! A comment possibly appended to the statement itself will be copied to the first line outputted to the share file (if SHARED's argument list is empty, only the comment will be written). In case a share file is written in C or Pascal format, one has to assure that the comment itself does not contain character sequences that close the comment (''*/'' resp. ''*)''). AS does not check for this!
valid for: all processors
This instruction inserts the file given as a parameter into the just as if it would have been inserted with an editor (the file name may optionally be enclosed with '' characters). This instruction is useful to split source files that would otherwise not fit into the editor or to create ''tool boxes''.
In case that the file name does not have an extension, it will automatically be extended with INC.
The assmebler primarily tries to open the file in the directory containing the source file with the INCLUDE statenemt. This means that a path contained in the file specification is relative to this file's directory, not to the directory the assembler was called from. Via the -i <path list> option, one can specify a list of directories that will automatically be searched for the file. If the file is not found, a fatal error occurs, i.e. assembly terminates immediately.
For compatibility reasons, it is valid to enclose the file name in '' characters, i.e.
include stddef51and
include "stddef51.inc"are equivalent. CAUTION! This freedom of choice is the reason why only a string constant but no string expression is allowed!
The search list is ignored if the file name itself contains a path specification.
valid for: all processors
BINCLUDE can be used to embed binary data generated by other programs into the code generated by AS (this might theoretically even be code created by AS itself...). BINCLUDE has three forms:
BINCLUDE <file>This way, the file is completely included.
BINCLUDE <file>,<offset>This way, the file's contents are included starting at <offset> up to the file's end.
BINCLUDE <file>,<offset>,<length>This way, <length> bytes are included starting at <offset>.
The same rules regarding search paths apply as for INCLUDE.
Though the assembler checks source files as strict as possible and delivers differentiated error messages, it might be necessary from time to time to issue additional error messages that allow an automatic check for logical error. The assembler distinguishes among three different types of error messages that are accessible to the programmer via the following three instructions:
These instructions generally only make sense in conjunction wit conditional assembly. For example, if there is only a limited address space for a program, one can test for overflow in the following way:
ROMSize equ 8000h ; 27256 EPROM ProgStart: . . <the program itself> . . ProgEnd: if ProgEnd-ProgStart>ROMSize error "\athe program is too long!" endifApart from the instructions generating errors, there is also an instruction MESSAGE that simply prints a message to the console resp. to the assembly listing. Its usage is equal to the other three instructions.
valid for: all processors
One could say that READ is the counterpart to the previous instruction group: it allows to read values from the keyboard during assembly. You might ask what this is good for. I will break with the previous principles and put an example before the exact description to outline the usefulness of this instruction:
A program needs for data transfers a buffer of a size that should be set at assembly time. One could store this size in a symbol defined with EQU, but it can also be done interactively with READ:
IF MomPass=1 READ "buffer size",BufferSize ENDIFPrograms can this way configure themselves dynamically during assembly and one could hand over the source to someone who can assemble it without having to dive into the source code. The IF conditional shown in the example should always be used to avoid bothering the user multiple times with questions.
READ is quite similar to SET with the difference that the value is read from the keyboard instead of the instruction's arguments. This for example also implies that AS will automatically set the symbol's type (integer, float or string) or that it is valid to enter formula expressions instead of a simple constant.
READ may either have one or two parameters because the prompting message is optional. AS will print a message constructed from the symbol's name if it is omitted.
valid for: all processors
By default, AS assigns a distinct syntax for integer constants to a processor family (which is in general equal to the manufacturer's specifications, as long as the syntax is not too bizarre...). Everyone however has his own preferences for another syntax and may well live with the fact that his programs cannot be translated any more with the standard assembler. If one places the instruction
RELAXED ONright at the program's beginning, one may furtherly use any syntax for integer constants, even mixed in a program. AS tries to guess automatically for every expression the syntax that was used. This automatism does not always deliver the result one might have in mind, and this is also the reason why this option has to be enable explicitly: if there are no prefixes or postfixes that unambiguously identify either Intel or Motorola syntax, the C mode will be used. Leading zeroes that are superfluous in other modes have a meaning in this mode:
move.b #08,d0This constant will be understood as an octal constant and will result in an error message as octal numbers may only contain digits from 0 to 7. One might call this a lucky case; a number like 077 would result in trouble without getting a message about this. Without the relaxed mode, both expressions unambiguously would have been identified as decimal constants.
The current setting may be read from a symbol with the same name.
valid for: all processors
END marks the end of an assembler program. Lines that eventually follow in the source file will be ignored. IMPORTANT: END may be called from within a macro, but the IF-stack for conditional assembly is not cleared automatically. The following construct therefore results in an error:
IF DontWantAnymore END ELSEIFEND may optionally have an integer expression as argument that marks the program's entry point. AS stores this in the code file with a special record and it may be post-processed e.g. with P2HEX.
END has always been a valid instruction for AS, but the only reason for this in earlier releases of AS was compatibility; END had no effect.
When writing the individual code generators, I strived for a maximum amount of compatibility to the original assemblers. However, I only did this as long as it did not mean an unacceptable additional amount of work. I listed important differences, details and pitfalls in the following chapter.
''Where can I buy such a beast, a HC11 in NMOS?'', some of you might ask. Well, of course it does not exist, but an H cannot be represented in a hexadecimal number (older versions of AS would not have accepted such a name because of this), and so I decided to omit all the letters...
''Someone stating that something is impossible should be at least as cooperative as not to hinder the one who currently does it.''From time to time, one is forced to revise one's opinions. Some versions earlier, I stated at his place that I couldn't use AS's parser in a way that it is also possible to to separate the arguments of BSET/BCLR resp. BRSET/BRCLR with spaces. However, it seems that it can do more than I wanted to believe...after the n+1th request, I sat down once again to work on it and things seem to work now. You may use either spaces or commas, but not in all variants, to avoid ambiguities: for every variant of an instruction, it is possible to use only commas or a mixture of spaces and commas as Motorola seems to have defined it (their data books do not always have the quality of the corresponding hardware...):
Bxxx abs8 #mask is equal to Bxxx abs8,#mask Bxxx disp8,X #mask is equal to Bxxx disp8,X,#mask BRxxx abs8 #mask addr is equal to BRxxx abs8,#mask,addr BRxxx disp8,X #mask addr is equal to BRxxx disp8,X,#mask,addrIn this list, xxx is a synonym either for SET or CLR; #mask is the bit mask to be applied (the # sign is optional). Of course, the same statements are also valid for Y-indexed expression (not listed here).
With the K4 version of the HC11, Motorola has introduced a banking scheme, which one one hand easily allows to once again extend an architecture that has become 'too small', but on the other hand not really makes programmers' and tool developers' lifes simpler...how does one sensibly map something like this on a model for a programmer?
The K4 architecture extends the HC11 address space by 2x512 Kbytes, which means that we now have a total address space of 64+1024=1088 Kbytes. AS acts like this were one large unified addres space, with the following layout:
In case one is not really sure if the current mapping is really the desired one, the pseudo instruction PRWINS may be used, which prints the assumes MMxxx register contents plus the current mapping(s), like this:
MMSIZ $e1 MMWBR $84 MM1CR $00 MM2CR $80 Window 1: 10000...12000 --> 4000...6000 Window 1: 90000...94000 --> 8000...c000An instruction
jmp *+3located at $10000 would effectively result in a jump to address $4003.
Of course, it is a bit crazy idea to add support in AS for a processor that was mostly designed for usage in work stations. Remember that AS mainly is targeted at programmers of single board computers. But things that today represent the absolute high end in computing will be average tomorrow and maybe obsolete the next day, and in the meantime, the Z80 as the 8088 have been retired as CPUs for personal computers and been moved to the embedded market; modified versions are marketed as microcontrollers. With the appearance of the MPC505 and PPC403, my suspicion has proven to be true that IBM and Motorola try to promote this architecture in as many fields as possible.
However, the current support is a bit incomplete: Temporarily, the Intel-style mnemonics are used to allow storage of data and the more uncommon RS/6000 machine instructions mentioned in [69] are missing (hopefully noone misses them!). I will finish this as soon as information about them is available!
Motorola, which devil rode you! Which person in your company had the ''brilliant'' idea to separate the parallel data transfers with spaces! In result, everyone who wants to make his code a bit more readable, e.g. like this:
move x:var9 ,r0 move y:var10,r3 ,is p****ed because the space gets recognized as a separator for parallel data transfers!
Well...Motorola defined it that way, and I cannot change it. Using tabs instead of spaces to separate the parallel operations is also allowed, and the individual operations' parts are again separated with commas, as one would expect it.
[64] states that instead of using MOVEC, MOVEM, ANDI or ORI, it is also valid to use the more general Mnemonics MODE, AND or OR. AS (currently) does not support this.
Regarding the assembler syntax of these processors, Hitachi generously copied from Motorola (that wasn't by far the worst choice...), unfortunately the company wanted to introduce its own format for hexadecimal numbers. To make it even worse, it is a format that uses unbalanced single quotes, just like Microchip does. This is something I could not (I even did not want to) reproduce with AS, as AS uses single quotes to surround ASCII character sequences. Instead, one has to write hexadecimal numbers in the well-known Motorola syntax: with a leading dollar sign.
Unfortunately, Hitachi once again used their own format for hexadecimal numbers, and once again I was not able to reproduce this with AS...please use Motorola syntax!
When using literals and the LTORG instruction, a few things have to be kept in mind if you do not want to suddenly get confronted with strange error messages:
Literals exist due to the fact that the processor is unable to load constants out of a range of -128 to 127 with immediate addressing. AS (and the Hitachi assembler) hide this inability by the automatic placement of constants in memory which are then referenced via PC-relative addressing. The question that now arises is where to locate these constants in memory. AS does not automatically place a constant in memory when it is needed; instead, they are collected until an LTORG instruction occurs. The collected constants are then dumped en bloc, and their addresses are stored in ordinary labels which are also visible in the symbol table. Such a label's name is of the form
LITERAL_s_xxxx_n .In this name, s represents the literal's type. Possible values are W for 16-bit constants, L for 32-bit constants and F for forward references where AS cannot decide in anticipation which size is needed. In case of s=W or L, xxxx denotes the constant's value in a hexadecimal notation, whereas xxxx is a simple running number for forward references (in a forward reference, one does not know the value of a constant when it is referenced, so one obviously cannot incorporate its value into the name). n is a counter that signifies how often a literal of this value previously occurred in the current section. Literals follow the standard rules for localization by sections. It is therefore absolutely necessary to place literals that were generated in a certain section before the section is terminated!
The numbering with n is necessary because a literal may occur multiple times in a section. One reason for this situation is that PC-relative addressing only allows positive offsets; Literals that have once been placed with an LTORG can therefore not be referenced in the code that follows. The other reason is that the displacement is generally limited in length (512 resp. 1024 bytes).
An automatic LTORG at the end of a program or previously to switching to a different target CPU does not occur; if AS detects unplaced literals in such a situation, an error message is printed.
As the PC-relative addressing mode uses the address of the current instruction plus 4, it is not possible to access a literal that is stored directly after the instruction, like in the following example:
mov #$1234,r6 ltorgThis is a minor item since the CPU anyway would try to execute the following data as code. Such a situation should not occur in a real program...another pitfall is far more real: if PC-relative addressing occurs just behind a delayed branch, the program counter is already set to the destination address, and the displacement is computed relative to the branch target plus 2. Following is an example where this detail leads to a literal that cannot be addressed:
bra Target mov #$12345678,r4 ; is executed . . ltorg ; here is the literal . . Target: mov r4,r7 ; execution continues hereAs Target+2 is on an address behind the literal, a negative displacement would result. Things become especially hairy when one of the branch instructions JMP, JSR, BRAF, or BSRF is used: as AS cannot calculate the target address (it is generated at runtime from a register's contents), a PC value is assumed that should never fit, effectively disabling any PC-relative addressing at this point.
It is not possible to deduce the memory usage from the count and size of literals. AS might need to insert a padding word to align a long word to an address that is evenly divisible by 4; on the other hand, AS might reuse parts of a 32-bit literal for other 16-bit literals. Of course multiple use of a literal with a certain value will create only one entry. However, such optimizations are completely suppressed for forward references as AS does not know anything about their value.
As literals use the PC-relative addressing which is only allowed for the MOV instruction, the usage of literals is also limited to MOV instructions. The way AS uses the operand size is a bit tricky: A specification of a byte or word move means to generate the shortest possible instruction that results in the desired value placed in the register's lowest 8 resp. 16 bits. The upper 24 resp. 16 bits are treated as ''don't care''. However, if one specifies a longword move or omits the size specification completely, this means that the complete 32-bit register should contain the desired value. For example, in the following sequence
mov.b #$c0,r0 mov.w #$c0,r0 mov.l #$c0,r0 ,the first instruction will result in true immediate addressing, the second and third instruction will use a word literal: As bit 7 in the number is set, the byte instruction will effectively create the value $FFFFFFC0 in the register. According to the convention, this wouldn't be the desired value in the second and third example. However, a word literal is also sufficient for the third case because the processor will copy a cleared bit 15 of the operand to bits 16..31.
As one can see, the whole literal stuff is rather complex; I'm sorry but there was no chance of making things simpler. It is unfortunately a part of its nature that one sometimes gets error messages about literals that were not found, which logically should not occur because AS does the literal processing completely on his own. However, if other errors occur in the second pass, all following labels will move because AS does not generate any code any more for statements that have been identified as erroneous. As literal names are partially built from other symbols' values, other errors might follow because literal names searched in the second pass differ from the names stored in the first pass and AS quarrels about undefined symbols...if such errors should occur, please correct all other errors first before you start cursing on me and literals...
People who come out of the Motorola scene and want to use PC-relative addressing explicitly (e.g. to address variables in a position-independent way) should know that if this addressing mode is written like in the programmer's manual:
mov.l @(Var,PC),r8no implicit conversion of the address to a displacement will occur, i.e. the operand is inserted as-is into the machine code (this will probably generate a value range error...). If you want to use PC-relative addressing on the SH7x00, simply use ''absolute'' addressing (which does not exist on machine level):
mov.l Var,r8In this example, the displacement will be calculated correctly (of course, the same limitations apply for the displacement as it was the case for literals).
The instruction set of these 4 bit processors spontaneously reminded me of the 8080/8085 - many mnemonics, the addressing mode (e.g. direct or indirect) is coded into the instruction, and the instructions are sometimes hard to memorize. AS or course supports this syntax as Hitachi defined it. I however implemented another variant for most instructions that is - in my opinion - more beautiful and better to read. The approach is similar to what Zilog did back then for the Z80. For instance, all machine instructions that transfer data in some form, may the operands be constants, registers, or memory cells, may be used via the LD instruction. Similar 'meta instructions' exist for arithmetic and logical instructions. A complete list of all meta instructions and their operands can be found in the tables 4.6 and 4.6, their practical use can be seen in the file t_hmcs4x.asm.
Meta Instruction | Replaces |
LD src, dest XCH src, dest ADD src, dest ADC src, dest SUB src, dest SBC src, dest OR src, dest AND src, dest EOR src, dest CP cond, src, dest BSET bit BCLR bit BTST bit |
LAI, LBI, LMID, LMIIY, LAB, LBA, LAY, LASPX, LASPY, LAMR, LWI, LXI, LYI, LXA, LYA, LAM, LAMD LBM, LMA, LMAD, LMAIY, LMADY XMRA, XSPX, XSPY, XMA, XMAD, XMB AYY, AI, AM, AMD AMC, AMCD SYY SMC, SMCD OR, ORM, ORMD ANM, ANMD EORM, EORMD INEM, INEMD, ANEM, ANEMD, BNEM, YNEI, ILEM, ILEMD, ALEM, ALEMD, BLEM, ALEI SEC, SEM, SEMD REC, REM, REMD TC, TM, TMD |
Operand | Types |
src, dest cond bit bitpos |
A, B, X, Y, W, SPX, SPY (register) M (memory addressed by X/Y/W) M+ (ditto, with auto increment) M- (ditto, with auto decrement) #val (2/4 bits immediate) addr10 (memory direct) MRn (memory register 0..15) NE (unequal) LE (less or equal) CA (carry) bitpos,M bitpos,addr10 0..3 |
The instruction set of the H16's core well deserves the label ''CISC'': complex addressing modes, instructions of extremely variable length, and there are many shortforms for instructions with common operands. For instance, several instructions know different ''formats'', depending on the type of source and destination operand. The general rule is that AS will always use the shortest possible format, unless it was specified explicitly: angegeben:
mov.l r4,r7 ; uses R format mov.l #4,r7 ; uses RQ format mov.l #4,@r7 ; uses Q format mov.l @r4,@r7 ; uses G format mov:q.l #4,r7 ; forces Q instead of RQ format mov:g.l #4,r7 ; forces G instead of RQ formatFor immediate arguments, the ''natural' argument length is used, e.g. 2 bytes for 16 bits. Shorter or longer arguments may be forced by an appended operand size (.b, .w, .l or :8, :16, :32). However, the rule for displacements and absolute addresses is that the shortest form will be used if no explicit size is given. This includes exploiting that the processor does not output the uppermost eight bits of an address. Therefore, an absolute address of $ffff80 can be coded as a single byte ($80).
Furthermore, AS knows the ''accumulator bit'', i.e. the second operand of a two-operand instruction my be left away if the destination is register zero. There is currently no override this behaviour.
Additionally, the following optimizations are performed:
Similar to the HMCS400, addressing modes are largely encoded (or rather encrypted..) into into the mnemonics, and also here I decided to provide an alternate notation that is more modern and better to read. A complete list of all meta instructions and their operands can be found in the tables 4.8 and 4.8, their practical use can be seen in the file t_olms4.asm.
Meta Instruction | Replaces |
LD dest, src DEC dest INC dest BSET bit BCLR bit BTST bit |
LAI, LLI, LHI, L, LAL, LLA, LAW, LAX, LAY, LAZ, LWA, LXA, LYA, LPA, LTI, RTH, RTL DCA, DCL, DCM, DCW, DCX, DCY, DCZ, DCH INA, INL, INM, INW, INX, INY, INZ SPB, SMB, SC RPB, RMB, RC TAB, TMB, Tc |
Operand | Types |
src, dest bit bitpos |
A, W, X, Y, Z, DPL, DPH (Register) T, TL, TH (Timer, obere/untere H"alfte) (DP), M (Speicher adressiert durch DPH/DPL) #val (4/8 bit immediate) PP (Port-Pointer) C (Carry) (PP), bitpos (DP), bitpos (A), bitpos 0..3 |
The data memory of these 4 bit controllers consists of up to 128 nibbles. However, only a very small subset of the machine instructions have enough space to accomodate seven address bits, which menas that - once again - banking must help out. The majority of instructions that address memory only contain the lower four bits of the RAM address, and unless the lowest 16 nibbles of the memory shall be addressed, the P register delivers the necessary upper address bits. The assembler is told about its current value via an
assume p:<value>statement, e.g. directly after a PAGE instruction.
Speaking of PAGE: both PAGE and SWITCH are machine instructions on these controllers, i.e. the do not have the function known from other targets. The pseudo instruction to start a SWITCH/CASE construct is SELECT in OLMS-50 mode, and the listing's page size is set via PAGESIZE.
The program memory of these microcontrollers is organized in pages of 128 words. Honestly said, this organization only exists because there are on the one hand branch instructions with a target that must lie within the same page, and on the other hand ''long'' branches that can reach the whole address space. The standard syntax defined by Mitsubishi demands that page number and offset have to be written as two distinct arguments for the latter instructions. As this is quite inconvenient (except for indirect jumps, a programmer has no other reason to deal with pages), AS also allows to write the target address in a ''linear'' style, for example
bl $1234instead of
bl $24,$34 .
Since the 6502's undocumented instructions naturally aren't listed in any data book, they shall be listed shortly at this place. Of course, you are using them on your own risk. There is no guarantee that all mask revisions will support all variants! They anyhow do not work for the CMOS successors of the 6502, since they allocated the corresponding bit combinations with "official" instructions...
The following symbols are used:
& | binary AND |
| | binary OR |
^ | binary XOR |
<< | logical shift left |
>> | logical shift right |
<<< | rotate left |
>>> | rotate right |
<- | assignment |
(..) | contents of .. |
.. | bits .. |
A | accumulator |
X,Y | index registers X,Y |
S | stack pointer |
An | accumulator bit n |
M | operand |
C | carry |
PCH | upper half of program counter |
Instruction | : | JAM or KIL or CRS |
Function | : | none, prozessor is halted |
Addressing Modes | : | implicit |
Instruction | : | SLO |
Function | : | M<-((M)<<1)|(A) |
Addressing Modes | : | absolute long/short, X-indexed long/short, |
Y-indexed long, X/Y-indirect | ||
Instruction | : | ANC |
Function | : | A<-(A)&(M), C<- A7 |
Addressing Modes | : | immediate |
Instruction | : | RLA |
Function | : | M<-((M)<<1)&(A) |
Addressing Modes | : | absolute long/short, X-indexed long/short, |
Y-indexed long, X/Y-indirect | ||
Instruction | : | SRE |
Function | : | M<-((M)>>1)^(A) |
Addressing Modes | : | absolute long/short, X-indexed long/short, |
Y-indexed long, X/Y-indirect | ||
Instruction | : | ASR |
Function | : | A<-((A)&(M))>>1 |
Addressing Modes | : | immediate |
Instruction | : | RRA |
Function | : | M<-((M)>>>1)+(A)+(C) |
Addressing Modes | : | absolute long/short, X-indexed long/short, |
Y-indexed long, X/Y-indirect | ||
Instruction | : | ARR |
Function | : | A<-((A)&(M))>>>1 |
Addressing Modes | : | immediate |
Instruction | : | SAX |
Function | : | M<-(A)&(X) |
Addressing Modes | : | absolute long/short, Y-indexed short, |
Y-indirect | ||
Instruction | : | ANE |
Function | : | M<-((A)&$ee)|((X)&(M)) |
Addressing Modes | : | immediate |
Instruction | : | SHA |
Function | : | M<-(A)&(X)&(PCH+1) |
Addressing Modes | : | X/Y-indexed long |
Instruction | : | SHS |
Function | : | X<-(A)&(X), S<-(X), M<-(X)&(PCH+1) |
Addressing Modes | : | Y-indexed long |
Instruction | : | SHY |
Function | : | M<-(Y)&(PCH+1) |
Addressing Modes | : | Y-indexed long |
Instruction | : | SHX |
Function | : | M<-(X)&(PCH+1) |
Addressing Modes | : | X-indexed long |
Instruction | : | LAX |
Function | : | A,X<-(M) |
Addressing Modes | : | absolute long/short, Y-indexed long/short, |
X/Y-indirect | ||
Instruction | : | LXA |
Function | : | X04<-(X)04&(M)04, |
A04<-(A)04&(M)04 | ||
Addressing Modes | : | immediate |
Instruction | : | LAE |
Function | : | X,S,A<-((S)&(M)) |
Addressing Modes | : | Y-indexed long |
Instruction | : | DCP |
Function | : | M<-(M)-1, Flags<-((A)-(M)) |
Addressing Modes | : | absolute long/short, X-indexed long/short, |
Y-indexed long, X/Y-indirect | ||
Instruction | : | SBX |
Function | : | X<-((X)&(A))-(M) |
Addressing Modes | : | immediate |
Instruction | : | ISB |
Function | : | M<-(M)+1, A<-(A)-(M)-(C) |
Addressing Modes | : | absolute long/short, X-indexed long/short, |
Y-indexed long, X/Y-indirect | ||
Microcontrollers of this family have a quite nice, however well-hidden feature: If one sets bit 5 of the status register with the SET instruction, the accumulator will be replaced with the memory cell addressed by the X register for all load/store and arithmetic instructions. An attempt to integrate this feature cleanly into the assembly syntax has not been made so far, so the only way to use it is currently the ''hard'' way (SET...instructions with accumulator addressing...CLT).
Not all MELPS-740 processors implement all instructions. This is a place where the programmer has to watch out for himself that no instructions are used that are unavailable for the targeted processor; AS does not differentiate among the individual processors of this family. For a description of the details regarding special page addressing, see the discussion of the ASSUME instruction.
As it seems, these two processor families took disjunct development paths, starting from the 6502 via their 8 bit predecessors. Shortly listed, the following differences are present:
65816 | MELPS-7700 | 65816 | MELPS-7700 |
---|---|---|---|
REP TCS TCD PHB WAI |
CLP TAS TAD PHT WIT |
PHK TSC TDC PLB |
PHG TSA TDA PLT |
Especially tricky are the instructions PHB, PLB and TSB: these instructions have a totally different encoding and meaning on both processors!
Unfortunately, these processors address their memory in a way that is IMHO even one level higher on the open-ended chart of perversity than the Intel-like segmentation: They do banking! Well, this seems to be the price for the 6502 upward-compatibility; before one can use AS to write code for these processors, one has to inform AS about the contents of several registers (using the ASSUME instruction):
The M flag rules whether the accumulators A and B should be used with 8 bits (1) or 16 bits (0) width. Analogously, the X flag decides the width of the X and Y index registers. AS needs this information for the decision about the argument's width when immediate addressing (#<constant>) occurs.
The memory is organized in 256 banks of 64 KBytes. As all registers in the CPU core have a maximum width of 16 bits, the upper 8 bits have to be fetched from 2 special bank registers: DT delivers the upper 8 bits for data accesses, and PG extends the 16-bit program counter to 24 bits. A 16 bits wide register DPR allows to move the zero page known from the 6502 to an arbitrary location in the first bank. If AS encounters an address (it is irrelevant if this address is part of an absolute, indexed, or indirect expression), the following addressing modes will be tested:
The automatic determination of the address length described above may be overridden by the usage of prefixes. If one prefixes the address by a <, >, or >> without a separating space, an address with 1, 2, or 3 bytes of length will be used, regardless if this is the optimal length. If one uses an address length that is either not allowed for the current instruction or too short for the address, an error message is the result.
To simplify porting of 6502 programs, AS uses the Motorola syntax for hexadecimal constants instead of the Intel/IEEE syntax that is the format preferred by Mitsubishi for their 740xxx series. I still think that this is the better format, and it looks as if the designers of the 65816 were of the same opinion (as the RELAXED instruction allows the alternative use of Intel notation, this decision should not hurt anything). Another important detail for the porting of programs is that it is valid to omit the accumulator A as target for operations. For example, it is possible to simply write LDA #0 instead of LDA A,#0.
A real goodie in the instruction set are the instructions MVN resp. MVP to do block transfers. However, their address specification rules are a bit strange: bits 0--15 are stored in index registers, bits 16--23 are part of the instruction. When one uses AS, one simply specifies the full destination and source addresses. AS will then automatically grab the correct bits. This is a fine yet important difference Mitsubishi's assembler where you have to extract the upper 8 bits on your own. Things become really convenient when a macro like the following is used:
mvpos macro src,dest,len if MomCPU=$7700 lda #len elseif lda #(len-1) endif ldx #(src&$ffff) ldy #(dest&$ffff) mvp dest,src endmCaution, possible pitfall: if the accumulator contains the value n, the Mitsubishi chip will transfer n bytes, but the 65816 will transfer n+1 bytes!
The PSH and PUL instructions are also very handy because they allow to save a user-defined set to be saved to the stack resp. to be restored from the stack. According to the Mitsubishi data book [51], the bit mask has to be specified as an immediate operand, so the programmer either has to keep all bit<->register assignments in mind or he has to define some appropriate symbols. To make things simpler, I decided to extend the syntax at this point: It is valid to use a list as argument which may contain an arbitrary sequence of register names or immediate expressions. Therefore, the following instructions
psh #$0f psh a,b,#$0c psh a,b,x,yare equivalent. As immediate expressions are still valid, AS stays upward compatible to the Mitsubishi assemblers.
One thing I did not fully understand while studying the Mitsubishi assembler is the treatment of the PER instruction: this instruction allows to push a 16-bit variable onto the stack whose address is specified relative to the program counter. Therefore, it is an absolute addressing mode from the programmer's point of view. Nevertheless, the Mitsubishi assembler requests immediate addressing, and the instructions argument is placed into the code just as-is. One has to calculate the address in his own, which is something symbolic assemblers were designed for to avoid...as I wanted to stay compatible, AS contains a compromise: If one chooses immediate addressing (with a leading # sign), AS will behave like the original from Mitsubishi. But if the # sign is omitted, as will calculate the difference between the argument's value and the current program counter and insert this difference instead.
A similar situation exists for the PEI instruction that pushes the contents of a 16-bit variable located in the zero page: Though the operand represents an address, once again immediate addressing is required. In this case, AS will simply allow both variants (i.e. with or without a # sign).
The M16 family is a family of highly complex CISC processors with an equally complicated instruction set. One of the instruction set's properties is the detail that in an instruction with two operands, both operands may be of different sizes. The method of appending the operand size as an attribute of the instruction (known from Motorola and adopted from Mitsubishi) therefore had to be extended: it is valid to append attributes to the operands themselves. For example, the following instruction
mov r0.b,r6.wreads the lowest 8 bits of register 0, sign-extends them to 32 bits and stores the result into register 6. However, as one does not need this feature in 9 out of 10 cases, it is still valid to append the operand size to the instruction itself, e.g.
mov.w r0,r6Both variants may be mixed; in such a case, an operand size appended to an operand overrules the ''default''. An exception are instructions with two operands. For these instructions, the default for the source operand is the destination operand's size. For example, in the following example
mov.h r0,r6.wregister 0 is accessed with 32 bits, the size specification appended to the instruction is not used at all. If an instruction does not contain any size specifications, word size (w) will be used. Remember: in contrast to the 68000 family, this means 32 bits instead of 16 bits!
The chained addressing modes are also rather complex; the ability of AS to automatically assign address components to parts of the chain keeps things at least halfway manageable. The only way of influencing AS allows (the original assembler from Mitsubishi/Green Hills allows a bit more in this respect) is the explicit setting of displacement lengths by appending :4, :16 and :32.
Thanks to John Weinrich, I now have the official Intel data sheets describing these 'grandfathers' of all microprocessors, and the questions about the syntax of register pairs (for 8-bit operations) have been weeded out for the moment: It is RnRm with n resp. m being even integers in the range from 0 to E resp. 1 to F. The equation m = n + 1 must be fulfilled.
The maximum address space of these processors is 4 Kbytes large. This address space is not organized in a linear way (how could this be on an Intel CPU...). Instead, it is split into 2 banks of 2 Kbytes. The only way to change the program counter from one bank to the other are the instructions CALL and JMP, by setting the most significant bit of the address with the instructions SEL MB0 resp. SEL MB1.
To simplify jumps between these two banks, the instructions JMP and CALL contain an automatism that inserts one of these two instructions if the current program counter and the target address are in different banks. Explicit usage of these SEL MBx instructions should therefore not be necessary (though it is possible), and it can puzzle the automatism, like in the following example:
000: SEL MB1 JMP 200hAS assumes that the MB flag is 0 and therefore does not insert a SEL MBO instruction, with the result that the CPU jumps to address A00h.
Furthermore, one should keep in mind that a jump instruction might become