(C) 2004-2005 Julián Albo.
Use and distribution allowed under the terms of the GPL license.
Last revision date: 19-apr-2005
Current Pasmo version: 0.5.2
Pasmo is a multiplatform Z80 cross-assembler, easy to compile and easy to
use. It can generate object code in several formats suitable for many
Z80 machines and emulators.
Pasmo generates fixed position code, can not be used to create relocatable
object files for use with linkers.
Pasmo is compatible with the syntax used in several old assemblers, by
supporting several styles of numeric and string literals and by
providing several names of the most used directives.
However, in Pasmo the Z80 mnemonics, register and flags names and
directives are reserved words, this may require changes of symbol
names conflicting in some programs.
Pasmo can also generate the 8086 equivalent to the z80 assembly code. It can create COM files for ms-dos, by using the binary generation mode, or CMD files for CP/M 86, by using the --cmd generation mode. This feature is experimental, use with care.
Download Pasmo from
http://www.arrakis.es/~ninsesabe/pasmo/.
Several binary executable are provided in the web, if your platform
is not between these, or wants a more recent version, you must
download the source package and compile it. If you want to compile it
in windows you can use cygwin or mingw with the Makefile provided, with
other compilers you may need to create a project, workspace or whatever
your compiler or IDE uses.
To compile you need gcc version 2.95 or later, with the c++ language
included (usually a package called g++-something).
Others compilers may also be used, any reasonable standard complaint
c++ compiler must compile it with few or none corrections.
From version 0.5.2 a configure script is provided. You can use the
usual './configure ; make ; make install' procedure.
You have also an official Debian package for testing and
unstable releases.
Pasmo is invoked from command line as:
pasmo [options] file.asm file.bin [file.symbol [file.publics] ]
Where file.asm is the source file, file.bin is the object file to be created and optionally file.symbol is the file where the symbol table will be written and file.publics is the file for the public symbols table. Both symbol file names can be an empty string for no generation or - to write in the standard output. When the --public option is used this is handled in another way, see below.
Options can be zero or more of the following:
When no option for code generation is specified, --bin is used by default.
The -d option is intended to debug pasmo itself, but can also be useful to find errors in asm code. When used the information is showed in the standard output. Error messages goes to error ouptut unless the --err option is used.
If none of the code generation options is specified, then --bin mode is used by default.
The --bin mode just dumps the code generated from the first position used without any header. This mode can be used for direct generation of CP/M or MSX COM files, supposed that you use a ORG 100H directive at the beginning of the code, or to generate blocks of code to be INCBINed in other programs.
The --hex mode generates code in Intel HEX format. This format can be used with the LOAD or HEXCOM CP/M utilities, can be transmitted more easily than a binary format, and is also used in some PROM programming tools.
The prl format is used in several variants of Digital Research CP/M operating system. In pasmo is supported only to create RSX files for use in CP/M Plus, use for PRL files in MP/M is not supported because I don't have a MP/M system, real or emulated, where to test it.
The --cmd option generates a CP/M 86 CMD mode, using the 8080 memory model of CP/M 86. Used in conjuction with the --86 option can easily generate CP/M 86 executables from CP/M 80 sources with minimal changes.
The --tap options generates a tap file with a code block, with the loading position set to the beginnig of the code so you can load it from Basic with a LOAD "" CODE instruction.
Same as --tap but using tzx format instead of tap.
The --cdt options generates a cdt file with a code block, with the loading position set to the beginning of the code and the start address to the start point specified in the source, if any, so you can use RUN "" to execute it or LOAD "" to load it.
With the --tapbas option a tap file is generated with two parts: a Basic loader and a code block with the object code. The Basic loader does a CLEAR before the initial address of the code, loads the code, and executes it if a entry point is defined (see the END directive). That way you can directly launch the code in a emulator, or transfer it to a tape for use in a real Spectrum.
Same as --tapbas but using tzx format instead of tap.
Same as --tapbas but using cdt format instead of tap and with a Locomotive Basic loader instead of Spectrum Basic.
Generate the object file in plus3dos format, used by the Spectrum +3 disks. The file can be loaded from Basic with a LOAD "filename" CODE instruction.
Generate the object file with Amsdos header, used by the Amstrad CPC on disk files. The file generated can be loaded from Basic with LOAD "filename", address or executed with RUN "filename" if an entry point has been specified in the source (see the END directive).
Generate the object file with header for use with BLOAD in MSX Basic.
The symbol table generated contains all identifiers used in the program, with the locals represented as a 8 digit hexadecimal number in order of use, unless the --public option is used. In that case only the symbols specified in PUBLIC directives are listed.
The symbol table format is a list of EQU directives. That way you can INCLUDE it in another source to create programs composed of several blocks.
Source code files must be valid text files in the platform used. The use of, for example, unix text files under pasmo in windows, is unsupported and the result is undefined (may depend of the compiler used to build pasmo, for example). The result of the use of a file that contains vertical tab or form feed characters is also undefined.
Everything after a ; in a line is a comment (unlees the ; is part of a string literal, of course). There are no multiline comments, you can use IF 0 .... ENDIF instead (but see INCLUDE).
String literals are written to the object file without any character set translation. Then the use of any character with a different meaning in the platform were pasmo is running and the destination machine must be avoided, and the code of the character may be used instead. That also means that using Pasmo in any machine that uses a non ascii compatible character set may be difficult, and that a source written in utf-8 may give undesired results. This may be changed in future versions of Pasmo.
A line may begin with a decimal number followed by blanks. This number is ignored by the assembler, is allowed for compatibility with old assemblers. The line number reported in errors is the sequential number of the line in the file, not this.
Blanks are significative only in string literals and when they separate lexical elements. Any number of blanks has the same meaning as one. A blank between operators and operands is allowed but no required except when the same character has other meaning as prefix ('$' and '%', for example).
Numeric literals can be written in decimal, binary, octal and hexadecimal formats. Several formats are accepted to obtain compatibility with the source format of several assemblers.
A literal that begins with $ is a hexadecimal constant, except if the literal is only the $ symbol, in that case is an operator, see below.
A literal that begins with # is a hexadecimal constant, except if there are two consecitives #, see the ## operator.
A literal that begins with & can be hexadecimal, octal or binary constant, depending of the character that follows the &: H means hexadecimal, O octal and X hexadecimal, if none of this the caracter must be a valid hexadecimal digit and the constant is hexadecimal.
A literal that begins with % is a binary constant, except if the literal is only the % symbol, in that case is an operator, see below.
A literal that begins with a decimal digit can be a decimal, binary, octal or hexadecimal. If the digit is 0 and the following character is an X, the number is hexadecimal. If not, the suffix of the literal is examined: D means decimal, B binary, H hexadcimal and O or Q octal, in any other case is taken as decimal. Take care, FFFFh for example is not an hexadecimal constant, is an identifier, to write it with the suffix notation you must do it as 0FFFFh.
All numeric formats can have $ signs between the digits to improve readability. They are ignored.
There are two formats of string literals: single or double quote delimited.
A string literal delimited with single quotes is the simpler format, all characters are included in the string without any special interpretation, with the only exception that two consecutive single quotes are taken as one single quote character to be included in the string. For example: the single quote delimited string 'That''s all folks' generates the same string as the double quote delimited "That's all folks".
A string literal delimited with double quotes is interpreted in a way similar to the C and C++ languages. The \ character is taken as escape character, with the following interpretations: n is a new line character (0A hex), r is a carriage return (0D hex), t is a tabulator (09 hex), a is a bell (07 hex), x indicates that the two next characters will be considered the hexadecimal code of a char and a char with that code is inserted, an octal digit prefixes and begins an octal number of up to three digits, and the corresponding character is inserted into the string, the characters \ and " means to insert itself in the string, and any other char is reserved for future use.
A string literal of length 1 can be used as a numeric constant with the numeric value of the character contained. This allows expressions such as 'A' + 80h to be evaluated as expected.
Identifiers are the names used for labels, EQU and DEFL symbols and macro names and parameters. The names of the Z80 mnemonics, registers and flag names, and of pasmo operands and assemble directives are reserved and can not be used as names of identifiers. Reserved names are case insensitive, even if case sensitive mode is used.
In the following 'letter' means an english letter character in upper or lower case. Characters that correspond to letters in other languages are not allowed in identifiers.
Identifiers begins with a letter, '_', '?', '@' or '.', followed for zero or more letter, decimal digit, '_', '?', '@', '.' or '$'. The '$' are ignored, but a reserved word with a '$' embedded or appended is not recognized as such.
Identifiers that begins with '_' are special when using autolocal mode, see the --alocal option and the chapter about labels for details.
Identifiers are case sensitive if the option --nocase is not used. When using --nocase, they are always converted to upper case.
File names are used in the INCLUDE and INCBIN directives. They follow special rules.
A file name that begins with a double quote character must end with another double quote, and the file name contains all character between them without any special interpretation.
A file name that begins with a single quote character must end with another single quote, and the file name contains all character between them without any special interpretation.
In any other case all characteres until the next blank or the end of line are considered part of the file name. Blank characters are space and tab.
A label can be placed at the beginning of any line, before any assembler mnemonic or directive. Optionally can be followed by a ':', but is not recommended to use it in directives, for compatibility with other assemblers. A line that has a label with no mnemonic nor directive is also valid.
The label has special meaning in the MACRO, EQU and DEFL directives, in any other case the value of the current code generation position is assigned to the label.
Labels can be used before his definition, but the result of doing this with labels assigned with DEFL is undefined.
The value of a label cannot be changed unless DEFL is used in all assignments of that label. If the value assigned to a label is different in the two passes of the assembly the program is illegal, but is not guaranteed that an error is generated. However, is legal to assign a value undefined in the first pass (by using an expression that contains a label not yet defined, for example).
In the default mode a label is global unless declared as LOCAL into a MACRO, REPT or IRP block, see the LOCAL directive for details.
In the autolocal mode, introduced by using the --alocal command line option, all labels that begins with a '_' are locals. His ambit ends at the next non local label or in the next PROC, LOCAL, MACRO, ENDP or ENDM directive.
Both automatic and explicit local labels are represented in the symbol table listing as 8 digit hexadecimal numbers, corresponding to the first use of the label in the source.
List of directives supported in Pasmo, in alphabetical order.
All numeric values are taken as 16 bits unsigned, using 2 complement or trucating when required. Logical operators return FFFF hex for true and 0 for false, in the arguments 0 is false and any other value true.
Parenthesis may be used to group parts of expressions. They are also used to express indirections in the z80 instructions that allows or require it. This can cause some errors when a parenthesized expression is used in a place were an indirections is allowed. Pasmo uses some heuristic to allow the expression to be correctly interpreted, but are far from perfect.
Using the bracket only mode the parenthesis have the unique meaning of grouping expressions, brackets are required for indirections, thus solving ambiguities.
Short circuit evaluation: the && and || operators and the conditional expression are short circuited. This means that if one of his operators need not be evaluted, it can include undefined symbols or divisions by 0 without generating an error (but still must have correct syntax). In the conditional expression this applies to the branch not taken, in the && operator to the second operand if the first is false, and in the || operator to the second operand if the first is true.
Table of operators by order of precedence, those in the same line have the same precedence:
## (see note) $, NUL, DEFINED *, /, MOD, %, SHL, SHR, <<, >> +, - (binary) EQ, NE, LT, LE, GT, GE, =, !=, <, >, <=, >= NOT, ~, !, +, - (unary) AND, & OR, |, XOR && || HIGH, LOW ?
The ## operator is an special case, is processed during the macro expansion, see the chapter about macros.
There are two types of macro directives: the proper MACRO directive and the repetition directives REPT and IRP. In addition the ENDM and EXITM directives controls the end of the macro expansion.
A macro parameter is an indentifier that when the macro is expanded is substitued by the value of the argument applied. The argument can be empty, or be composed by one or more tokens. If a MACRO is defined inside another macro directive the external parameters are not substitued, with the other macro directives the parameter substitution is done beginnig by the most external directive. The NUL operator can be used to check if an argument is not empty. The .SHIFT directive can be used to work with an undeterminated number of arguments.
Identifier pasting: inside a MACRO the operator ## can be used to join two idenfiers resulting in another identifier. This is intended to allow the creation of identifiers dependent of macro arguments.
IRP parameter, argument list.Repeats the block of code between the IRP directive and his corresponding ENDM one time for each of the arguments.
name MACRO [ list of parameters]or:
MACRO name [ , list of parameters]In all cases, list of paramenters is a comma separated list of identifiers, and name is the name assigned to the macro created.
The assumption of Pasmo if that, being a cross-assembler, it will be
used on a machine with many available resources. Then I do not make
any effort to provide means to do things that can be easily made
with other utilities, unless I think (or other people convince me)
that including it in Pasmo can be much more convenient.
For example, if you want to create a sin table you can write a program
in your favourite language that writes a file with the table and
INCLUDE that file, and if you want to automate that type of things you
can use make.
Taken that into account, I am open to suggestions to improve Pasmo and to patches that implements it. In the later case please take care to write things in a portable way, without operating system or compiler dependences.
Pasmo has a simple code generator that uses absolute address of memory. That will make difficult to adapt it to generate relocatable code for use with linkers. I don't have plans to do it for the moment, maybe someone want to contribute?
Some people suggested to add support for Game Boy programming. There are two problems, the simplified way used to generate code in Pasmo, and my inexistent knowledge of the Game Boy.
Thanks to all people that has made suggestions and notified or corrected bugs. And to these that show me the beautiful things they do with Pasmo.
You can use Pasmo to convert any binary file to .tap, just write a tiny program called for example convert.asm:
ORG address_to_load_the_file INCBIN file.bin
Assemble it with: pasmo --tap convert.asm file.tap, and you have it. The same may be done for the other formats supported.
Pasmo emits a warning when using a expression that looks line a non
existent z80 instruction, such as 'ld b, (nn)', but the simplified
way used to detect that also warns in cases like
'ld b,(i1+i2)*(i3+i4)'.
A way to avoid the warning in that case is to prefix the expression
with parenthesis with '+' or '0 +'.
Using the bracket only mode the problem does not exist, in that case
the parenthesis are always taken as expresssions (and the programmer
is supposed to know that), thus the warning is not emitted.
More suggestions about that are wellcome.
There is no way to include a file whose name contains blanks, single and double quoutes. Someone use file names like that?
That's all folks!
Send comments and criticisms to:
julian.notfound@gmail.com