|
Sunday, 17-Nov-2024 13:42:11 GMT Z88 Programmers Section Z80asm - Z80 Module Assembler (running as native Z88 application or cross assembler)The Z88 already contains a small in-line assembler built into the BBC BASIC application. However, it is a limited developers environment since all the source has to be part of the BBC BASIC program. On an expanded Z88 this allows you to sizes of max. 40K BASIC programs (of source text) that needs to be loaded into the BBC BASIC application, then compiled into machine code inside the same memory space of the BBC BASIC application. The end result is not much. BBC BASIC gives an opportunity to develop small machine code utilities to complement the BBC BASIC interpreter. The Z80 Assembler application on the Z88 (and cross assembler versions on other platforms) enables you to compile ASCII text source files into machine code. Z80asm is an integrated Z80 parser, linker and library manager. The following features is available:
Project setup for modular file compilation and linking Application projects can be split into modular format and combined during linking into a single Z80 native executable. Each source file module has its own name and scope of local identifiers. Each module can reference to external identifiers that in turn has been defined as a global identifier in a particular source file module. Scope of identifiers are managed using the XDEF (define global identifier) and XREF (reference external identifier) directives. Each module can be specified on the command line or in a project file containing all (file)names of the project. The organisation of the files in a project also determine the location in the compiled code. Facilities controlled using UNIX-alike command line interface The syntax of the assembler parameters is a straightforward design. Filenames or a project file are always specified. The options may be left out: z80asm [options] <filename {filename}> | <#modulesfile> As seen above <options> must be specified first. Then you enter the names of all files to be assembled. You either choose to specify all file names or a #<modulesfile> containing all file names. File name are specified without the 'asm extension. This is added automatically by the assembler. As seen on the syntax at least on source file must be specified and may be repeated with several file names. Only one project file may be specified if no source file names are given. Many of the parameters are pre-set with default values which gives an easy user interface when specifying the assembly parameters. Only advanced parameters need to be specified explicitly. The help page displays the default parameter values at the bottom of the page (help page is displayed when no arguments are specified at the command line. The assembler options are used to control the assembly process and output. They are recognised by the assembler when you specify a leading minus before the option identifier ('-'). Options are always specified before file names or project files. When the assembler is executed options are all pre-set with default values and are either switched ON or OFF (active or not). All options have a single letter identification. Upper and lower case letters are distinguished, which means that 'a' might be different command than 'A'. If an option is to be turned off, you simply specify a 'n' before the identification, e.g. -nl which selects listing files not to be created by the assembler. The following list displays the all available options:
Date stamp controlled assembly To facilitate large projects, date stamp controlled assembly is unavoidable. The Z80asm application itself if a large project of nearly 1MB in size. Without the ability to compile only the last modified source, it would have been a pain to develop it. With no updated source files compared to the corresponding object files, the assembler automatically begins the linking process (if linking has been enabled). On-line help for all directives and other facilities in the Z88 "Z80asm" application The Z80asm application on the Z88 has been provided with 24 help topics containing compact descriptions of all available assembler command line options, directives in source files, assembler principles and much more. With the on-line help you can easily acquire the simply interface of an otherwise complex application. Pressing the <HELP> key display all 24 items to be selected by <ENTER>. However, everything is not explainable in 24 simple topics. You will find a 100K comprehensive documentation of Z80asm on the supplied discs. The Assembler is based around the principle of a two pass assembly of the source files. The first stage performs all syntax parsing, source file manipulation (Include files, conditional assembly) and code generation. Pass 2 evaluates all forward referenced names and expressions. During pass 1 and 2 the object file is generated. The object file is the basis of the linking process to generate the executable code. The object file contains unresolved expressions to references of external identifiers referenced in other source modules, local and globally defined identifiers and the raw code generation of the source module without address referencing. All parsing is performed using dynamic data structures for symbol tables and other control structures. The outcome of this is unlimited compilation - the only barrier is your computer's memory and disc storage capacity (this applies both for the Z88 and Cross assembler version). The complete source including OZ definition files and other header files was actually compiled on the Z88 using 1MB RAM for testing purposes. At maximum, the assembler had allocated 350K during the linking process for all internal data structures of the complete project. Every bit of the RAM card was exploited to perform the complete process of producing the executable application code of the Z88 version of Z80asm. The 2 pass file parsing allows you to create symbol table output files (address relative) and listing files (containing line numbers and intermediate hex dump of generated Z80 code in front of each line). The information in listing files is a combination of the original source file and additional information of the generated machine code. Further, the listing file is page formatted with a header specifying the date and time of the compilation, the file name of the listing and a page number. For each line of the source file the following information is written: <source file line number><assembler address><machine code hex dump><source line> The machine code and assembler address output are written in hexadecimal notation. If the machine code uses more the 4 bytes, the source file line is written on the following line. This usually happens when you have defined a string constant or a relatively large amount of constant definitions. The assembler address is always beginning at zero, i.e. the beginning of the current modules' machine code. In a relocated context of the machine code, where all code is positioned at fixed addresses, you will have the opportunity to view the addresses of your code relative to the start of individual modules using the assembler output addresses. Further, the last assembler address can be interpreted as the size of the modules' generated machine code. Listing files also serves the purpose of a hard copy on paper of your programs, and are useful in a debugging phase (identifying opcodes versus the mnemonic representation of instructions). Symbol tables contains the integer form of symbolical names and constants that has been parsed and generated during a compilation of a source file. The structure of the symbol table is divided into two columns. The first contains the parsed symbol names, converted to upper case. The second column contains the generated value of the symbol name. All symbol values are displayed in signed 32 bit hexadecimal notation. The two columns are separated by tabulators which represents a default value of 8 spaces per tabulator. The width of the symbol name column is defined as the tabulator distance multiplied by 4. The default with of the name column is 4 * 8 = 32 spaces. The symbol table will be written to the end of the appropriate listing file, if listing file and symbol table output is enabled. If no listing file output is enabled, the symbol table will be written to a separate file, identified with the base name of the source file module and given the 'sym' extension. Error files are created by the assembler during processing. If any errors should occur, they will be written to this file containing information of where the error occurred in the source module. If no errors were found during parsing of the current source file module, the error file is automatically closed and deleted. Multiple line feed support (CR, LF and CRLF) on Z88 version of assembler The Z80asm application supports three line feed standards - probably the only known at all. The reason for this is the pure simplicity of transferring your source files from one system to the Z88 with modification. Without automatic line feed support you would need to convert source files to the local Z88 standard before compiling. The Z80asm on Z88 support LF (UNIX standard) and CR (Z88 standard) as single line feed characters, and CRLF as extended line feed (the MS-DOS/Windows standard). Line feed conversion would in fact not be difficult anyway, because you can easily specify these in EazyLink during transfer. The only problem is to remember to actually perform it and not get different line feed standard mixed in your source files. Free format of assembler source files The source files may be written in a free format. No fixed position columns as needed as in the COBOL programming language. All text may be typed in mixed case (the assembler converts all text input to upper case). Tabulators may be used freely (in stead of spaces which also saves source file space) to suit the programmers own habits of structured text layouts. However, one rule must be obeyed: syntax of Z80 assembler mnemonics and most directives must be completed on individual lines. Conditional assembly using multilevel IF, ELSE, ENDIF directives One of the advantages of conditional assembly is to produce different versions of your application using simple identifier mnemonics and logical markings in the source. The Z80asm has been implemented with this feature using a straightforward model of the IF - ELSE - ENDIF clause. Each IF may be expressed with a constant or a true non-forward referenced expression. Inside each sub-block you may place your source. Only the source inside the IF-ELSE-ENDIF block that evaluates to the true-false nature of the IF expression is parsed. You may perform as many nesting of conditional assembly as needed. There is no limit to the amount of nesting levels. Conditional assembly is perfect for INCLUDE files; e.g.. the same constant definitions but placed differently on the various platforms of your project (Z88, PC ...). Multiple level INCLUDE file support (for header files or other source files) An important facility in a source file developing system is to extract definitions in external "header" files. This saves spaces and makes it easier to adjust constant values that affect the whole project. The Z80 Assembler has implemented the INCLUDE directive which allows you to merge external source files at the current position within the current source file being parsed. You may also specify other INCLUDE directives inside your included files. The only limit necessary is to avoid including files that also includes the current file - which would create an endless loop and an application crash. Loading of external BINARY files into code (specified as directive in source) Most of the time all developments of your project is source code containing assembler mnemonic and data structure definitions using a mixture of data directives like DEFB, DEFW and DEFS. However, sometimes it may be necessary to specify an external source of data that is not immediately available as source text. Using the BINARY directive you may merge external binary data from a file into the current position of the code (defined by the current relative position of the code generation of pass 1). Data directives, data structures templates and enumerations The second most important feature in an assembler (apart from symbolic assembler instructions and address labels) is the ability to define data or data structures. Z80asm implements all the necessary data directives: DEFB <8bit expr> {,<8bit
expr>} (-128; 255)
Directives for defining constants: DEFGROUP '{' name {',' name ['=' <8bit expression>]} '}' Defines a group of identifier symbols with implicit values. This is similar to the enumeration principles used in C and PASCAL. The initial symbol value is 0, increased by 1 for each new symbol in the list. DEFC name=<32bit expression>{, name=<32bit expression>} Define a symbol constant. The allowed range is a signed 32 bit integer value. The expression must be evaluable, i.e. no forward referencing identifiers. All standard Z88 operating system header files use DEFC. DEFINE name,{name} Defines a symbol identifier as logically true (integer <> 0). The symbol will be created as a local variable and disappears when assembly is finished on the current source file module. Nice feature for conditional assembly. DEFS <16bit expression> Allocates storage. The size of the area will be filled with zero bytes. DEFVARS <16bit expression> Defines variable address area or offsets that first defines the origin of a variable area. This may be defined using an evaluable 16 bit expression (positive). Each variable name is followed by a size specifier which can be a byte, word, pointer (3 bytes) or long word size. This is particularly useful for defining dynamic data structures in linked lists and binary search trees. Defining variable areas are only template definitions not allocations. An example:
the following is useful for defining dynamic data structures:
Compact object file format for intermediate code used by inbuilt linker Object files are very compact compared to the original source file. The main purpose of object files is to avoid continuous recompilation of source files even if they haven't been altered (the idea behind date stamp control). The next important factor is size. On the Z88 a developer needs every byte possible. Using a compact format it may be possible to compile relative large application projects on the Z88. You could for example compile all your source files into object code, then remove the source files from memory to obtain sufficient space to link all modules into executable code. The next obvious reason for object files is the need to keep information about the assembled module for the linking stage (local, global identifiers, expressions and unresolved addresses). Some assembler actually perform everything at once but on the price of huge processing time and space. The object file format is documented. Developers are welcome to add new features to the object file format. Code generation of ZILOG's undocumented Z80 instructions We have included the syntax parsing and code generation of the undocumented Z80 instructions for the sake of completeness. However, IM 2 interrupts must be disabled before they are executed (an interrupt may otherwise occur in the middle of the instruction execution). Many games on the ZX Spectrum have used them to protect the code from prying eyes. The Z88 native debugger code uses some of the undocumented instructions for fast access to register variables. They are as follows: LD r,IXL r = A,B,C,D,E,IXL,IXH (SLL = [S]hift [L]ogical [L]eft). SLL does shift leftwards but insert a '1' in bit 0 instead of a '0'. Except for the SLL instruction all have bugs related to an interrupt being able to occur while the instructions are decoded by the processor. They are implemented on the chip, but are reported to be unreliable. We have used some of them in our debugger software for the Z88. Until now the code has been running successfully on all our Z88 computers. Freeform expressions allowed for all instruction constants (addresses, operands), arithmetic operators Expressions is almost unavoidable in source files. They define and explain things much clearer than just using a constant. The Z80 Module Assembler allows expressions wherever a parameter is needed. This applies to Z80 mnemonic instructions, directives and even in character strings. The resulting value from an evaluated expression is always an integer. All expressions are calculated internally as 32 bit signed integers. However, the parameter type defines the true range of the expression. E.g. you cannot store a 32 bit signed integer at an 8 bit LD instruction like LD A, <n> . If a parameter is outside an allowed integer range an assemble error is reported. Finally, no floating point operations are needed by the assembler. There is no real standard on Z80 based computers. Whenever an integer is stored in a Z80 file, the standard Zilog notation is used, i.e. storing the integer in low byte, high byte order (this also applies to 32 bit integers). This standard is also known as little endian notation (also used by INTEL processors). All basic arithmetic operators are supported: addition, subtraction, multiplication, division and modulus. In addition binary logical operators are implemented: binary AND, OR and XOR. The following is the complete list : + addition, e.g. 12+13 With relational operators you may form logical expressions resulting in true or false conditions. The resulting value of a true expression is 1. The resulting value of a false expression is 0. These operators are quite handy when you need to perform complex logic for conditional assembly in IF-ELSE-ENDIF statements. The following relational operators are available: = equal to You may link several relational expressions with the binary operators AND, OR and XOR. You have all the possibilities available! It is perfectly legal to use relational expressions in parameters requiring an arithmetic value. For example: LD A, (USING_IBM = 1) | RTMFLAGS The first linking stage of the assembler scans all available object files in the project for identifiers (both local and global), creating symbol tables and loading of the raw Z80 code. The machine code is gradually added together to form the complete executable code. Pass 2 evaluates all expressions from all modules and performs the address patching of the complete executable code. When both passes are complete, the completed code is saved to a file and an address map file is generated (if selected) that contains all label references defined with the actual address in memory when the Z80 code is executed. Create global address definition file As with address map files this contains information of globally declared symbolical address labels, relocated to their absolute position as for the compiled machine code file. However, the format is completely different; all symbols are created as constant definitions to be included as a header file into another source file and assembled. This is useful if you want to call subroutines compiled separately in another project (originated in a different memory setup). Z80 relocatable code generation The Z80 processor instruction set allows only relative jumps in maximum +/- 128 bytes using the JR and DJNZ instructions. Further, there is no program counter relative call-to-subroutine or jump-to-subroutine instruction. If you want a program to be address-independent no absolute address references may be used in jump or call instructions. If you want to program Z80 address independent code you can only write small routines using the JR and DJNZ instructions. With a restricted interval of 128 bytes you can imagine the size of those routines! Programming of large applications using address independency is simply impossible on the Z80 processor with the basic instruction set available. You can only define a fixed address origin (ORG) for your machine code to be loaded and executed from. However, there is one solution: before the code is executed an automatic address-relocation is performed to the current position in memory. This is done only once. A small routine must be included with the program to read the relocation information and patch it into the specified locations of the program. You can generate address independent code using the -R option on the command line. There is no other requirements. The relocatable code may be useful for programmers using the Cambridge Z88 who want to use machine code in the BBC BASIC application environment. This can easily be interfaced with the DIM statement to allocate memory for the machine code program, and issue a CALL or USR() to execute the machine code. The principle of relocation is in fact a self-modifying program. You cannot relocate a program that has been blown into an EPROM (cannot be modified). You may only execute relocatable programs in dynamic memory (RAM). The relocater is built into the Z80 Module Assembler. The relocation table is created during the linking phase. When all object modules have been linked and the appropriate machine code generated, the process is ended with first copying the relocater routine into the executable file, then a relocation table and finally the compiled machine code program. Any defined ORG in your code is superseded - this is not necessary in a relocatable program! The address map is an invaluable information during a debugging session of your compiled program. This file contains all symbolical address labels with their generated address constants after a completed linking/relocation of all modules into executable machine code. The map file is ordered in two groups; the first list contains all symbol names ordered alphabetically with corresponding address constants, the second list contains all symbols ordered by their address value (in chronological order). Creation of library files (searchable object files) The Z80 Assembler also enables you to develop libraries that you can include into your application projects during linking. Libraries are an organisation of object files. Each object has a name definition that is used to find the module during linking if another module references it. Libraries are usually organised in a project file containing all filenames of the modules. Modules benefit from being organised in topological order for optimal lookup. Compatibility across platforms Source files are usually no problem between platforms. However, Z80asm on the Z88 supports 3 different new line formats - <CR>, <LF> and <CRLF>. This enables you to easily port your sources from your main computer and continue compilation without changing the line feed format of your sources. The compiled object files can be ported to the platform of the Z80 cross assembler and linked to produce the native code. The object file format is also documented to enable for further development or external utilities that may manipulate the object files. Object files are small and thus easy to transfer between platforms. For software questions, ask the Z88 Development Team© Rakewell Limited 1998-2004 InterLogic 1999
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|