This file contains omissions/corrections which will be incorporated into the version 5.0 documentation at a later date. =================================================================== TABLE OF CONTENTS (1) Assembly Language Topics -- Version 5.0 Enhancements -- Writing Assembly Language Modules for C -- The CSECT Directive -- Relative Addressing (2) Linker Topics -- New Features of Blink -- Introduction to Overlays -- Custom Overlay Managers (3) Creating Load-and-Stay Resident Programs (4) Compiler-Defined Preprocessor Symbols (5) Direct Calling of Amiga Libraries -- Coding Library Calls for In-Line Code Generation (6) Using the FD2PRAGMA Program (7) Built-In Functions (8) Header File Compression -- Header File Organization (9) Library Documentation Corrections (10) Additional Notes On The Source Level Debugger (11) New Keywords in 5.0 (12) Library Descriptions (13) Description of Binary Patch Program (14) 68881 Notes ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::: 1 ::::::::::::: ASSEMBLY LANGUAGE TOPICS ::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: The assembler included in this package has many new features. Addressing Modes. Listed below are all the addressing modes supported. Mode Example Notes Dn add.w d1,d0 An addq.w #1,a1 (An) add.w (a1),d0 (An)+ add.w (a1)+,d0 -(An) add.w -(a1),d0 d16(An) add.w 10(a1),d0 d8(An,Xn) add.w 10(a1,a2.l),d0 bd(An,Xn) add.w $10000(a1,a2.l),d0 68020 only ([bd,An],Xn,od) add.w ([10,a1],a2.l,20),d0 68020 only ([bd,An,Xn],od) add.w ([10,a1,a2.l],20),d0 68020 only (xxx).W add.w (100).w,d0 (xxx).L add.l (100).l,d0 # add.l #100,d0 d16(PC) add.w 10(PC),d0 d8(PC,Xn) add.w 10(PC,a2.l),d0 bd(PC,Xn) add.w $10000(PC,a2.l) 68020 only ([bd,PC],Xn,od) add.w ([10,PC],a2.l,20),d0 68020 only ([bd,PC,Xn],od) add.w ([10,PC,a2.l],20),d0 68020 only where d8 is an eight bit number d16 is a sixteen bit number bd is a 32 bit byte displacement od is the 32 bit outer displacement An is an address register (a0-a7) Dn is a data register (d0-d7) Xn is an index register (a0.w-a7.l) Note: All the operands on the addressing modes marked 68020 are optional. Data Formats. New data formats are allowed for 68881 floating point instruction. These include: #2.1 #2.1E+10 These will be converted into the proper floating point formats according to the type of instruction. For example, in the following instruction: fmove.s #2.1,fp1 2.1 will be in single precsion. Other sizes allowed are: fmove.d #2.1,fp1 double precision fmove.x #2.1,fp1 extended precision Note that the packed data format is not converted for you. Also if you want to specify the bit pattern by hand you can use the following formats: fmove.d #$12345678,fp1 32 bit pattern fmove.s #$123456781234568,fp1 64 bit pattern fmove.x #$123456781234567812345678,fp1 96 bit pattern You can also specify the constants in octal (ie @123456712) or binary (ie %0110110100110101). Opcodes. All the 68020 opcodes have been added. A new size for all the branch instructions has been added. The size fields allowed on branches are demostrated below. BSR.S or BSR.B generate 8 bit offsets BSR.W generates 16 bit offsets BSR.L generates 32 bit offsets (Note that this is only supported on the 68020 and that BSR.L to externs are not allowed.) BSR with no size will calculate the size needed, 16 bit for externs. *NOTE that this is different from the way the previous version of the assembler worked. For example, the old assembler would make the instruction BRA.L FOO a 16 bit branch. This version makes a 32 bit branch which is only supported on the 68020. ___________________________________________________________________ ------------------------------------------------------------------- WRITING ASSEMBLY LANGUAGE MODULES FOR C You can write assembly language modules for inclusion in C programs if you are sufficiently experienced with the 68000 instruction set and if you observe the following: 1. The statements defining the functions should be placed in the text section by preceding them with: CSECT text This is not an absolute requirement, since the functions will be accessible regardless of the section in which they are defined, but it assures them of placement with the C functions during linking (if the -s option is used). Each function entry must be declared in an XDEF statement: XDEF AFUNC . . . FUNC EQU * 2. If the module is to define data locations to be accessed (using extern declarations) in C modules, those definitions should be placed in the data section by preceding them with: CSECT data Each data element must be declared in an XDEF statement in order to be accessible in the C modules: XDEF D0,D1,D2 D0 DC.L $4000 D1 DC.W $8000 D2 DC.L D0 3. Any of the registers D2-D7 or A2-A6 must be preserved by the module, and the return value loaded into the appropriate data registers. To call a C function from an assembly language module, you must include an XREF declaration for the function. Before calling the function (via JSR), you must supply any expected arguments in the proper order. After control returns from the called function, the stack pointer must be adjusted to account for pushed arguments. XREF cfunc MOVE.L DO,-(A7) ;push argument MOVE.L D1,-(A7) JSR cfunc ;call function ADDQ #8,A7 ;restore stack ptr Data elements defined in a C module may be accessed via XREF statements, as well: XREF XD2,XD3 . . . MOVE.L XD2,D0 ___________________________________________________________________ ------------------------------------------------------------------- THE CSECT DIRECTIVE All of the standard 68000 instructions are supported, as described in the Motorola manual. Assembly directives are discussed in the following paragraphs. Instead of the section directive, the Lattice assembler uses the CSECT directive of the form: CSECT name where name is the control section name. Note that a CSECT directive must precede any data-defining statements (i.e., there is no default control section). The Lattice 68000 macro assembler uses the concept of named control section to control the location of data and text during linking; the CSECT directive defines the start of a relocatable control section of the specified name. Note that "text" and "data" are used for compatibility with object files generated by the C compiler. Only one instance of a CSECT directive with a particular name is permitted; that is, scattered definitions of data for a CSECT are not allowed. The CSECT directive may be used to specify additional information about the control section; its general format is: CSECT name,type,align,rtype,rsize Only the name parameter must be present; it specifies the name of the control section. type is zero for code sections, one for data sections, and 2 for uninitialized data sections (like the udata section used by the C compiler); the default value is zero (code). align specifies the alignment requirements of the control section as a power of two; thus, align equal to one means the section will begin on an even address, and a value of two means an address divisible by 4, etc. This parameter is non-functional on the Amiga. All sections are long-word aligned. The last two parameters specify the type and size of relocation information associated with the address of any data in the control section. rtype specifies the relocation type, which determines the meaning of the address, or relocation data; the default value is zero. rsize specifies the size, in bytes, of the relocation data for the section; the default value is four. The legal types and sizes of relocation information on the 68000 are summarized in the following table: Type Size (bytes) Description 0 2 or 4 Absolute short or long address 1 2 PC-relative offset 2 2 Address-register-relative offset Type 0 relocation results in an absolute address, while type 1 relocation is computed as the difference between the referenced address and the location of the reference. Type 2 relocation is used to assign offsets for address-register-relative addressing of data. Both the base section used for such addressing and any bias placed in the address register are used when a load module is constructed. Note that symbols declared in XREF statements within a control section will be addressed using the relocation type and size defined for that section. The use of CSECT directives which are compatible with the -b and -r options of the C compiler are described later. ___________________________________________________________________ ------------------------------------------------------------------- RELATIVE ADDRESSING The Lattice assembler can be used to generate assembly language modules which are compatible with the conventions used in C programs compiled with the -b option on lc1 or the -r option on lc2. PC- relative external function calls can be forced using sequences such as the following: CSECT TEXT,0,,1,2 XREF cfunc XDEF afunc afunc JSR cfunc(PC) RTS This mechanism is the same as that used for function calls in code compiled with the -r option on lc2. Note that the XREF statement for the external function being called must appear after the CSECT statement, and that all calls must be made in the PC-relative addressing mode. If the XREF statement for the external function precedes the CSECT statement a standard JSR to a four-byte external address can be used. Address-register-relative addressing of data can be forced if the data is placed in a control section such as the following: CSECT data,1,,2,2 XREF xdata data1 DC.W 10 data2 DC.L 20 It then must be addressed using the appropriate address register: MOVE.W data1(A5),DO MOVE.L data2(A5),D1 MOVE.L d1,xdata(A5) this mechanism is the same as that used for addressing data in code compiled with the -b option on lc1. Note that the XREF statement for any external data elements being addressed must appear after the CSECT statement, and that all data references must be made in the address-register-indirect-with-displacement addressing mode. If the XREF statement for the external data precedes the CSECT statement, a standard reference to a four-byte external address can be used. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::: 2 ::::::::::::::::::: LINKER TOPICS :::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: NEW FEATURES OF BLINK 1. Strip Debugging Information with Blink Blink will allow the stripping of debugging information given an executable that contains debugging info. To do this type: blink oldfile TO newfile NODEBUG where oldfile is an executable file that contains debugging information, and newfile is a new executable file that will be stripped of debugging information. 2. Generate Programs that can be made Resident To generate an executable file that can be made resident simply link with the startup file CRES.O, CATCHRES.O, or CATCHRESNR.O instead of the normal startup file C.O. The executable file will have the PURE bit set. There are some rules that must be followed when doing this. -- Never use -b0 when compiling the file. -- All data hunks must be mergeable. -- There should be no absolute references to data. -- Use #pragmas instead of AMIGA.LIB when possible. In some cases it may be necessary to link with AMIGA.LIB. This is ok as long as BLINK doesn't generate any warning about references to absolute symbols. The following diagram describes how resident programs run. An old executable looks When running resident like this: programs: |--------------------| |---------------------| | | | | | CODE | | CODE | | | | | |--------------------| |---------------------| | | | | | MERGED DATA | | MERGED DATA | | | | (used only during | |--------------------| | load ) | | MERGED BSS | |---------------------| |--------------------| . . . |----------------------| | Segmented Allocated | | by cres.o and used | | by running program | | Referenced off of A4 | | | |----------------------| Each time the program is run a new data segment is created, old data is copied into it, and relocations are performed inside it. When the program terminates, the segment is released. The special symbols that blink creates are as follows: _LinkerDB pointer to data section RESOFF offset that LinkerDB is in data (0 or 0x8000) RESLEN number of longwords in data NEWDATAL number of bytes in new data segment including BSS The number of relocs in the data segment is stored just after the data segment, with the relocs following this. ___________________________________________________________________ ------------------------------------------------------------------- INTRODUCTION TO OVERLAYS Overlays are code groups and data hunk groups which reside in main memory only while in use. When one loaded group is no longer needed and another is needed, the memory used by the old group is reclaimed, and may be used to load in the new group. This group loading and unloading is performed automatically. Overlays on the Amiga are organized as a tree structure with nodes. Each node consists of a group of code hunks and data hunks. The tree consists of a ROOT NODE, which is permanently resident, and one or more OVERLAY NODES. The root node usually contains the libraries, your main program, and extensively used subroutines, etc. The overlay nodes consist of hunks which are to be loaded only as needed to conserve memory space. Figure 1: Overlay Tree Root | ------------------- overlay1 overlay2 | -------------------- overlay3 overlay4 | overlay5 In the overlay tree in Figure 1, overlay nodes 1 and 2 execute independently, using the same memory, one at a time. Also, if overlay node 2 is loaded, it can then load either of nodes 3 and 4, which share a block of memory in which they can execute. Thus, if 3 is loaded and executing, 4 and 1 are NOT loaded, overlay node 2 is loaded, and overlay 5 may be loaded. If overlay node 1 is loaded and executing, then nodes 2 through 5 are NOT currently in memory. Your overlay tree need not be a binary tree, balanced, or of a depth greater than 1. You can have 50 overlay nodes, all hanging from the root node. The nodes may be all mutually exclusive and independent of each other, or one long chain of overlay nodes. You must decide which of your routines can operate independently of each other. Overlays are not a general-purpose mechanism. They are intended primarily for large applications (100k or more), which must run in a small amount of memory in order to leave more room for data. Such applications should also have a great deal of modularity, with a definite calling hierarchy among the code routines. One good example (except for size) is a linker. Although it does not use overlays, it is feasible for blink to have overlays. If you run blink with the VERBOSE option, you see messages about "Parsing, Pass1," and so on. These represent clear phases of linking that are independent of each other--the only continuity is in the internal data structures they create and manipulate. To maximize available memory, either for the linker's data structures or for other concurrent programs and their data, blink could have been linked to use overlays, allowing no more than about 5k to 10k resident in memory at any given moment. The sole overhead would be a slight drop in speed performance due to the individual loading of the separate overlay nodes. Fortunately, since blink is relatively small and efficient in memory usage, it does not use overlays. 1. The Overlay Manager Overlays depend on the overlay manager. Every time a routine in an overlay node is referenced, a call is made to the Overlay Manager, which then scatter loads the desired overlay node, unloading any existing nodes which exist on another, mutually exclusive branch of the tree. These calls to the Overlay Manager are done by blink automatically and are effectively invisible to your own code. The Overlay Manager can be put into the libraries rather than kept as a separate file, as under ALINK. You can also override the original Overlay Manager with one of your own, thanks to an extension in blink. (See Custom Overlay Managers later in this text for more information on this.) 2. Using Overlays When using overlays, you should design your overlay tree after you have an idea of the calling hierarchy of your application. Those routines which are called frequently, or which are called by almost every other routine in your application, should be placed in the root node. Generally, the more commonly accessed the routine, the closer to the root node it should be. Major independent routines however, should be placed in nodes which are more in accordance with the general calling hierarchy. If you do not come up with an efficient tree, your program will merely be a little unorganized; an incorrect tree will result in a blink warning message. -------------------------------------------------------------------- | REMEMBER: Always put your main program into the root node (using | | either FROM or ROOT). Putting the main program in an overlay | | node will not work. | -------------------------------------------------------------------- 3. Overlay Node Restrictions There are restrictions when referencing routines across overlay nodes as illustrated in Figure 2. Figure 2: Overlay Tree Example Root (a, b, c) | -------------------- overlay1 overlay2 (d, e) (f, g) | -------------------- overlay3 overlay4 (h) (i) | overlay (j) -- Any of the overlay routines (d, e, f, g, h, i, j) can call any of the root node routines (a, b, c). -- Any of the root node routines (a, b, c) can call any of the first overlay level routines (d, e, f, g). -- Routines d and e can call each other and any root node routines. -- Routines f and g may call each other, any of the root node routines, or routines h and i. -- Routines h may call routines f, g, j, or any of the root node routines. -- Routines i may call routines f and g, or any of the root node routines. -- Routine j may call routines a, b, c, f, g, and h and only those routines. 4. WITH Files Similar to ALINK, blink uses WITH files to inform the linker that you want overlays--the syntax is the same as for ALINK. After the keyword OVERLAY, you should list the file or files of your node or nodes. Use one line per node with as many files as you want per node. For line continuation, use an asterisk * at the end of the line which continues on the next line. The nodes (i.e. file name groups) should be listed in pre-order fashion. One other factor you should understand about the syntax is asterisks (*). In addition to acting as a continuation symbol at the end of a continued WITH line of file overlays, they are also used at the beginning of a new line IF THE DEPTH OF THE NODE BEING LISTED IS GREATER THAN A NUMERIC VALUE OF ONE. As an example, the tree described in Figure 1 would look like this in WITH file syntax: OVERLAY overlay1 overlay2 *overlay3 **overlay5 *overlay4 Note the `#'-- this terminates the list as does an End-Of-File. Also note that since overlays 1 and 2 are at depth 1, they have no asterisks. The depth of a node is specified by the number of asterisks. The depth of an overlay in the tree should be equal to the number of asterisks plus one. 5. The MAP File When overlays are used, appropriate sections of the map file will include the information generated by the overlay nodes. Overlay references and symbols will appear in the map file sections to which they apply. The map file sections specific to overlays is obtained using the O option to the MAP keyword. This generates a section very much like the hunk section provided by the H option. The O option has two additional fields which give the overlay level and overlay ordinate of the overlay node to which the code and data hunks belong. If you use the O option but have no overlays, nothing is produced. ___________________________________________________________________ ------------------------------------------------------------------- CUSTOM OVERLAY MANAGERS You can write your own overlay manager if you need one. A cycle of compile (or more likely, assemble) accomplishes this task. Include it with the command: OVLYMGR file The object file must contain the symbol _ovlyMgr. This is exactly how the symbol must appear in the object file; the character case is important. You may need to omit the leading underscore, since Lattice C and the assembler both put it in for you. The code hunk must also have a section name of NTRYHUNK. Where it is necessary to have a code hunk which absolutely MUST be the very first hunk in the file, no matter what the linker tries to do, you should use the section name NTRYHUNK. blink always forces the section named NTRYHUNK to be the first hunk. However, this hunk is never merged with any other hunk, and there can only be one such hunk. Also, use of this section name for your own code segment will mean that you CANNOT use overlays (unless the segment is your own overlay manager), but you should try to avoid using this. You do not have to use OVLYMGR to use the section name NTRYHUNK--just list it as another object file. Note that the first hunk is also the program entry point. For further information about writing your own custom overlay manager, examine hunk 0 of a program which uses overlays. It does exactly what is necessary to do the overlays and to handle the entry point problem. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :: 3 ::::: CREATING LOAD-AND-STAY RESIDENT PROGRAMS :::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: This version of the compiler provides an alternative startup module named CBack.o which allows you to create Load-and-Stay-Resident (LSR) code. We strongly recommend that you use CBack.o for ANY program that: -- has no requirement for the original CLI window for the purposes of output, OR -- needs to be memory resident (such as POPCLI or MEMWATCH). The design specification of CBack.o means that the programmer only has to make minimal amendments to existing code in order to take advantage of LSR code. Linking with CBack.o requires the addition of some definitions to your main module and an awareness of the setting up which CBack.o carries out. The first step is the addition of some global variables necessary for CBack.o to determine how your task will run: LONG _stack Stack space for the created task CHAR *_procname Name of the process to be created LONG _priority The priority to create the process at LONG _BackGroundIO The flag to indicate an I/O requirement Most LSR programs will probably only need a stack size of 4000 bytes (the system default), although this figure of 4000 may need to be experimented with depending on the LSR application. There are no restrictions related to the name, although common sense dictates that a name which is easily recalled is probably best. The task priority should be set to 0, once more a baseline figure which may need to be varied. The _BackGroundIO flag is special flag required when you wish to perform any type of I/O to the originating console. When this flag is initialized to 0, your code is expected to carry out its own I/O to a separate window--or no I/O as the case may be. The latter is also true for any program which opens its own window. The initializing of _BackGroundIO to a value other than 0 informs CBack to open the default console and pass it to you in the global variable _Backstdout. It's the programmer's responsibility to close this file. ----------------------------------------------------------------- | WARNING: Failure to close this file means that the original | | window from which the program was run will NEVER CLOSE. Also, | | until you close the file handle, the window will remain open. | ----------------------------------------------------------------- While this may be useful for printing out logon banners, diagnostic messages or results of commands, a window which cannot be closed under any circumstances other than a system reboot is indicative of a low standard of programming. _Backstdout is an AmigaDOS file handle and consequently all I/O to this file handle must be performed with the Write() function--NOT the Lattice functions of write or fprintf. Closure must be only be done with Close(). Where a program establishes a resident task to communicate with, the file handle should be passed to this task for it to print messages on the CLI window. When the _Backstdout file handle is described as a NULL, your program cannot perform any output to the window. This can occur when the program was invoked from the Workbench environment or from another program which did not have a standard output window available at the time. The following example demonstrates a do-nothing program which uses CBack to install itself: #include #include #include #include long _stack = 4000; /* a reasonable amount of stack space */ char *_procname = "my_program"; long _priority = 0; /* run at standard priority */ long _BackGroundIO = 1; /* perform background I/O */ extern BPTR _Backstdout; /* file handle we will write to with */ #define MSG "Hello World!" void main(argc, argv) int argc; char *argv[]; { if (argc == 0) /* called from Workbench, can't do anything */ _exit(0L); if (_Backstdout) /* called from CLI - print out the string */ { Write(_Backstdout, MSG, strlen(MSG)); Close(_Backstdout); } } Where an LSR program operates as a single task for the entire system (such as POPCLI or MEMWATCH, it should check for the existence of a global message port. If a global message port does not exist, then the LSR program should create one. After creation of the port, any control information should be directed through this port. Where a global message port already exists then this should serve as the primary port for control information. The sample code for POPCLI and MEMWATCH demonstrate how this can be achieved. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :: 4 ::::::: COMPILER-DEFINED PREPROCESSOR SYMBOLS ::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: The Lattice AmigaDOS Compiler by default assigns the following preprocessor symbols: M68000 = 1 AMIGA = 1 LATTICE = 1 LATTICE_50 = 1 Using these compiler options will define the following preprocessor symbols: Option Preprocessor symbol -d DEBUG = 1 -w SPTR = 1 w/o -w LPTR = 1 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :: 5 ::::::::: DIRECT CALLING OF AMIGA LIBRARIES ::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: In general, the bulk of an Amiga C program will consist of various library calls. Improvements to this version of the compiler mean that library routines do not have to go through the stub routines in amiga.lib, although the option remains for those who wish to still do this. This gives the advantage of suppressing the code to push the function parameters on to the stack, calling the stub routine which places them into the appropriate register and then calls the library. The compiler can be informed which registers to place the parameters into and this allows it to select the optimal method of register loading. There are two advantages to avoiding the use of the amiga.lib stub routines: -- Code execution is faster than code using stub routines. -- Development time is saved by the avoidance of linking. The code generated by this technique does not usually show any significant saving in size over code generated by the conventional Amiga.Lib route. However, the stub routines can account for a large proportion of a small program and the execution speed advantage can become important. Calling of these direct functions is done through the inclusion of the prototype header files. These are located on Compiler Disk #2 with the calling syntax of: #include To use the Open() function of AmigaDOS, the calling sequence would look like this: #include ___________________________________________________________________ ------------------------------------------------------------------- CODING LIBRARY CALLS FOR IN-LINE CODE GENERATION It should be realized that the techniques used for the library function calls are not hard coded for the known Amiga functions. This means that these function calls may be freely used in any source code, although it is necessary to code a statement of the form: #pragma libcall where: #pragma part of the special calling routine. libcall part of the special calling routine. the pointer to the library base. the name of the library routine you intend to directly call. the offset from the library base of the routine. the description of the parameters. An example call using the Open() function of AmigaDOS would take this form: #pragma libcall DOSBase Open le 2102 where: DOSBASE the library pointer. Open the name of the function being called. le the offset from the library base as a hexadecimal value. 2102 the value used by the compiler to determine the register conventions. Note that the offsets from the library base actually have negative values, this is ignored in the interests of clarity. The calculation of the magic portion is arrived at by breaking down the number into nibbles representing the register conventions. The format of the magic number is: 4321r# where the numbers 4, 3, 2, and 1 indicate the registers where the fourth, third, second and first parameters respectively, are to be placed. The values to use for these are: HEX DATA HEX ADDRESS VALUE REGISTER VALUE REGISTER 0 D0 8 A0 1 D1 9 A1 2 D2 A A2 3 D3 B A3 4 D4 C A4 5 D5 D A5 6 D6 E A6 7 D7 F A7 Note that address registers A5 to A7 are never used in the above context. Observant programmers may recognize this pattern as that assigned by Motorola to the registers in the processor instruction set. The `r' portion of the magic number indicates the register where the result is returned. This is usually 0 for data register D0, although there are no restrictions as to which register you may select for return values. The `#' value indicates the total number of parameters for passing to the routine. The high-order bit (0X8) of this nibble indicates that the routine to be called unreasonably destroys the contents of data registers D6 and D7. In most cases you may ignore this nibble completely since it is intended to provide a Kickstart version 1.1 compatibility with the Text() and DoIO() functions. Let's return to our example function call: #pragma libcall DOSBase Open le 2102 Examining the magic and offset values, you will see how these figures were arrived at by an examination of vol.2 of the Amiga ROM Kernel Reference Manual page A-122. The synopsis of the Open() function is: file = Open( name, accessMode ) D0 D1 D2 Reference to the previous table will show that data registers D2, D1 and D0 have the values of 2, 1 and 0 respectively, and this forms the first part of the value 2102. If you examine the function OpenLibrary() on page A-124 in vol.2 of the Amiga ROM Kernel Manual, the synopsis is: library = OpenLibrary(libname, version) D0 A1 D0 The first part of our magic value would then be 090, the second part parameter value is 2, thus the final calculated value is 0902. The offset values can be obtained from the v1.2 .fd files on the v1.2 Extras disk. Alternatively, the columns starting on page D-5 of vol.1 of the Amiga ROM Kernel Manual provide these. For our example, the offset value is shown on page D-6 in exec.lib and has a value of 228. Note that the current edition of vol.1 of the Amiga ROM Kernel Manual has several errors in the .fd files listing. One of the most readily identifiable errors shows the incorrect bias for the DOS library -- (add the decimal value 30 or hexadecimal value 1E to correct the respective columns. There are also several exec.lib functions missing from the end of the exec.lib listing. A good policy is to always check the .fd files on your disks rather than rely solely on the reference manuals. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: : 6 ::::::::::: USING THE FD2PRAGMA PROGRAM ::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: The format of the #pragma statements for library calls is relatively easy to understand and code, it is difficult to maintain and or decode. We have provided a utility which will take a standard ASCII .fd file and convert this file to the appropriate #pragma statements. The .fd file should consist of single statements per line and structured in the following form: -- A line to be processed as a directive starts with two ## (hash) symbols. -- An asterisk (*) at the start of a line symbolizes a comment line. -- Any other line indicates entry points into the library. The directives for the conversion utility are: ##base _name Identifies the global variable pointing to the base of the library. ##bias n Identifies the offset value between the library base and the initial vector in the library. ##public Identifies subsequent lines as being available to programmers. ##private Identifies subsequent lines as being available to the library only. ##end Marks the end of the .fd file. The entry points into the library take the form: entryname(arguments)(registers) with commas separating the individual arguments and registers. The latter may also be separated by a slash character `/'. Some example entries look like this: InitCode(startClass,version)(D0/D1) PrintIText(rp,itext,left,top)(A0/A1,D0/D1) The first example is from exec.library while the second example is from intuition.library -- note the mixing of commas and slashes on the register descriptions. We strongly suggest that you always maintain and update the .fd file, running it through the FD2PRAGMA utility rather than trying amend the #pragmas themselves. This technique is less likely to lead to errors. Conversion of the .fd file is done by a command line of the following syntax: fd2pragma alpha.fd beta.h where: alpha.fd the .fd file for conversion. beta.h the new header file after conversion. Any errors encountered in the conversion will be printed on the standard output file. Typical errors would be invalid directives, invalid register specifications or no ##end statement. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: : 7 ::::::::::::::::: BUILT-IN FUNCTIONS ::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: This version of the compiler includes several built-in functions designed to generate high quality 68000 code. These functions are encouraged by the ANSI standard over others which might have previously been used. The built-in functions are: strlen memset strcmp memcmp strcpy memcpy The compiler will automatically recognize a built-in function provided it is prefixed by the string: __builtin_ The #defines in the header files string.h and stdlib.h have been modified to accomplish the invocation of a built-in function. To take advantage of these functions it is necessary to include the correct header file -- for these functions it is string.h file. In situations where you do not want to use the built-in functions, you can modify the appropriate function with an #undef. An additional built-in function has been provided to assist in the speedier development of small programs. This is: printf When this function is called, the compiler examines the formatting string: -- When it is a constant string with no substitutions, the compiler changes the printf call to a _write call. -- When it is a constant string AND only contains %s, %d and %p formats, a call is made to _tinyprintf. The purpose behind this is to allow programmers to use printf for output without having to write a trimmed version which does not handle the unused formats. The advantage of built-in functions is related to the ability of the compiler to exploit register contents. For example, the strlen call will generate no code if the input string is a constant string. In addition, functions cannot return the result pointer if it is unused -- all of this tends to a regime which produces significantly less code than an equivalent call to the library function. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: : 8 ::::::::::::: HEADER FILE COMPRESSION :::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: The utility lcompact can be used to compress header or source files in a form that can be read much faster by the compiler. The utility operates by encoding commonly occurring Amiga keywords. Although ANSI is quite specific about the processing of header or include files we believe this is a method of increasing the overall compiler performance without sacrificing ANSI compliance. To use the utility, you need to feed the source file from stdin and send the result to stdout. The command line syntax looks like this: lcompact dest We have supplied the header files in both compressed and uncompressed forms. There is no difference in the handling of these -- you do not have to specify anything special on your command lines or in the #include statement. You may also have compressed and uncompressed header files within the same directory. HEADER FILE ORGANIZATION There is now a new subdirectory with the header files called proto. This subdirectory contains a number of header files which contain function prototypes and the magic material which allow the Amiga Library functions to operate. If you are calling an Amiga function, you should include the appropriate header file. However, if you want to be lazy, you can use the syntax: #include This will include all the header files although an increase in compilation time will be the result. Where the system configuration contains expanded memory and everything is being held in memory, the increase in compilation time is probably academic. Note that these files correspond directly to the .fd files since they were constructed from them. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: : 9 ::::::::::::::Library Documentation Errors:::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: The following modifications were made to the Libraries and Header files after the documentation went to print. These changes are designed to take advantage of the the new ANSI "void *" keyword. By using "void *" instead of "char *", it will no longer be necessary to put casts in front of arguments and return values. Function Page Documentation Correction =============== ==== ======================== calloc, malloc L19 change "char *b;" to "void *b;" realloc, free realloc L19 return value "nb" should be type "void *" getmem, getml L122 both return a "void *" erand48,nrand48, L44 The variable seed[3] should be type jrand48,seed48, "unsigned short", pseed, should be lcong48 type "unsigned short *", and parm[7] should be type "unsigned short". This change is to maintain compatiblity with System V UNIX. LCMFFP.LIB G16 The lcmffp.lib DOES contain all of the floating point functions that are in the standard Lattice math library. The sample call to "emit" on page L51 should read emit(0x4180); Also, there is a typo on page v of the Preface to the manual. The reference to the new keyword "_stdards" should read "__stdargs". Note, all keywords prefaced with a single underscore should be prefaced with a double underscore. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: : 10 :::::::Additional Notes On The Source Level Debugger:::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: IF YOU HAVE ONLY 512K Bytes of MEMORY READ THIS FIRST BEFORE USING CodeProbe!!!! CodeProbe requires a large amount of memory to run both itself and an application. Typically you will want to have at least 300K available before invoking CPR. There are several things that you may wish to do to minimize the likelyhood of low memory situations. 1) Invoke CPR with the "-t cpr.tmp" option. CPR creates a temporary file for quick access to symbolic information that it reads from the application's executable file. This file can grow to approximately the same size as the executable file. By default this file is placed in "ram:". The -t option suggested above will cause the file to be placed in your current directory. This will be a little slower, however, it will require less memory. 2) Invoke CPR with the "-w" option. This option will tell CPR to open its windows on the WorkBench Screen rather than on its own custom screen. This will also save memory. For example to invoke the ftoc example program under limited memory conditions, a user might type: cpr -w -t cpr.tmp ftoc Several additions were made to CodeProbe after the manuals went to print. The File Menu has an additional Item called "Find Current Line". This option will automatically cause the source window to find the line of program execution. Both the "Find Current Line" and "Line" options will now appear as "Find Current Address" and "Address" when the debugger is in Assembler mode. When prompted for an address, you should enter a hexadecimal value. A '0x' prefix is not required. The Source window will disassemble code beginning at the specified address. The Options menu also has an addition item called "Step Into ResLib". This option allows the user to decide whether or not to allow CPR to step into resident libraries while tracing or running an application with Watch Breaks. This option defaults to "off". It should never be turned on unless you wish to debug your own resident libraries. Tracing into system libraries such as Exec or Intuition can lead to problems. As long as this option remains off, it is safe to run with Watch Breaks on while stepping over system resident libraries. The mouse can be used to perform a "go line" or "go address" command by double clicking the left mouse button with either shift key pressed. This compliments the default double click action of "break line" or "break address". There are several Documentation Errors: There is reference to a -n option on page D130. No such option has been implemented in this release. If you wish to catch a task started from outside of the debugger, you will have to bring up some other program. You will not of course have to run it. The information about the Symload command on pages D130 and D131 is incorrect. This command can take several forms: symload proc "process name" will find the executable file and the loaded memory segments associated with the specified process if it has CLI structure. (Note that "symload" and "proc" are required keywords in the above example). If the command has an executable file name as an additional argument, any debug information associated with that file will be loaded instead of looking for the file associated with the process. The address of a process task block can be specified in place of the process name. Symload can also be given a pointer to a segment list as an argument: symload seg 0x123456 c:executable Note that this form requires the keywords "symload" and "seg" as well as a the hexadecimal address pointing to the process seglist. The name of an executable file is also required. An ARexx interface is supported by CPR. Macros with a ".cpr" extension are recognized. CPR commands return status codes to ARexx as follows: 0 The debugger command was successful. 1 The command was syntactically correct, but failed for some other reason. 2 A syntax error occurred. 3 The debugger could not allocate memory for the ARexx message. In addition to the above error codes, an ARexx macro can receive the output of a debugger command as it would normally appear in the Dialog Window by invoking the ARexx command "options results". The output will then be accessable from the variable "result", provided that the debugger returned a 0 return code. A number of useful macros are provided on disk 4 in the ":examples/debugger" directory. These examples simulate "wack" commands. For additional information on these macros, read the file CprARexx.DOC on disk 4. If you have any useful ARexx macros you would like to share with other Lattice users please upload them to the Lattice Bulletin Board Service or the BIX network. We hope that you find release 1.0 of our Source Level Debugger beneficial for your development needs. We have incorporated many suggestions from our users into the current release, ARexx support is one example. We will continue to make enhancements to the product, and we hope that you will continue to help shape the future of this tool by offering comments and suggestions. We look forward to hearing from you. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: : 11 ::::::::::::::::::New Keywords in 5.0::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: The new compiler introduces several new keywords: The simplest of them is the keyword 'signed'. It works exactly like the unsigned keyword to ensure that a particular variable is not unsigned. While this is the default, the -cu option will cause characters to be treated as 'unsigned'. example: signed char c; extern signed int x; The 5.0 compiler provides several keywords to indicate the storage class of an object. With the 4.0 compiler, the only way to change the storage class was to use the -b0 or -b1 flag in conjunction with the -a option on LC2. These options are still available, but the recommended method is to let the compiler default to near addressing (-b1) and then use the keywords where necessary. Unlike MS-DOS based compilers, these keywords do not affect the size of an object, but instead indicate the storage class. In that vein, you MUST place the keyword as close to the data item as possible: extern int __near x; /* addressed as a4 relative */ extern long __far y; /* addressed with a 32 bit absolute */ extern struct Custom far custom; /* "" */ extern char * near y; static int far y; /* Place in the far section */ Note that you can not use the storage keyword except immediately before the target object. extern far int wrong; /* <<< Illegal */ static near char *nogood; /* <<< Not allowed */ The most important of these new storage class keywords is the chip (or __chip) keyword. static struct Image chip myimage = { ... }; extern struct Image chip pict; This will cause the compiler to place the target object into memory addressable by the Amiga custom chips. It is also important to note that that storage class keywords allow two versions: __far and far. The version with the double underscores is intended to allow for an ANSI conforming program while we offer the non-ANSI version for more readable code and some compatibility with MS-DOS based products. The 5.0 compiler also provides a number of keywords that may be applied to functions to permit special calling conventions. Of importance are the __regargs and __stdargs keywords which indicate that the compiler is to use an altered calling convention. By default, the compiler will use __regargs for everything. However, if you use the -rr option of LC1, the compiler will use a __regargs convention in which the first two data items and first two pointer items are passed in d0/d1 and a0/a1 respectively. The keywords allow you to override the default. For example: extern int __stdargs myprintf(); long __regargs foo(int i) { ... } __stdargs void bar(); These keywords may appear anywhere before the identifier to which they are applied. With the 4.0 compiler, it was necessary to use the -y option on LC2 in order to load up the address of the global data base at entry to a function. This however applied to all functions in a module. To cause it to be loaded for a single function, you can use the keyword __saveds as in: int __saveds myentry() { .... } Note that __saveds only has meaning when applied to the actual definition of the function. External functions with the __saveds keyword simply ignore the keyword. Like the __saveds keyword, the __interrupt keyword is applied to a function to indicate that this function may be called from an interrupt routine. Although at this release it currently does not affect the code generated for the function, it is provided for potential variations in code necessary to support interrupts on non-Amiga compilers. Providing the most control over register passing, the __asm keyword allows you to specify exactly which registers parameters are to be passed in. It can be used for both function definitions and function declarations: extern int __asm mymax( register __d0 int i, register __d1 int j); int __asm myfun( i,p ) register __d0 int i; register __a1 char *p; In order for the register specifier sequence to be used, you must have the __asm keyword specified on the function. If you do use the __asm keyword, you MUST specify a register for all parameters and not reuse the same register for any two parameters. If you need to pass some parameters on the stack then you should consider using the __regargs keyword instead. In order to permit the most flexibility in register passing, the compiler does not limit what registers may be passed. However this can lead to some situations which would make it impossible to generate code that works in the presence of aliased variables. To ensure that such situations are not presented, you should avoid utilizing registers that would normally assigned as register variables and instead stick to the registers: __d0 __d1 __d2 __d3 __a0 __a1 __a6 The best advice is to be careful when using this feature and if you are uncomfortable with it, utilize the -rr option of LC1 (or __regargs) in which the compiler will pick the best registers. The keywords __asm, __stdargs, and __regargs are mutually exclusive. *** Please note, the double underscores *** ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: : 12 :::::::::::::::::::Library Descriptions::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cback.o Startup routine to detach process from CLI and run in background. catchres.o Startup routine to catch software exceptions in resident programs. lcs.lib Lattice C Library for use with 16-bit integers. catch.o Startup routine to catch software exceptions. lcms.lib Lattice Standard IEEE Math Library for use with 16-bit integers. lcm881.lib Lattice 68881 Coprocessor Math Library. c.o Standard Lattice Startup routine. lcnb.lib Lattice C Library for use with no base-relative data addressing. catchresnr.o Startup - Catch Exceptions, Resident, No Requesters on exception. lcmffp.lib Lattice Motorola Fast Floating Point Math Library. lcmieee.lib Lattice IEEE Math Library for use with Commodore Resident Library. lcsr.lib Lattice C Library for use with 16-bit integers and Registerized Parameters. lcr.lib Lattice C Library for use with Registerized Parameters. lcmr.lib Lattice IEEE Math Library for use with Registerized Parameters. lc.lib Lattice Standard C Library. cres.o Startup routine for Resident Programs. lcm.lib Lattice Standard IEEE Math Library. lcsnb.lib Lattice C Library for 16-bit integers and no base-relative addressing. ddebug.lib Commodore debug library for use with Parallel Port. debug.lib Commodore debug library for use with Serial Port. amiga.lib Library of linkage routines to Amiga Resident Libraries. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: : 13 ::::::::::::::::::Binary Patch Program:::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Lattice traditionally releases software "patches" to correct problems which are found. Included on Disk 3 is a binary patch utility named "PATCH" which may be used to apply future patches (provided by Lattice) to the compiler and tools. The calling sequence is as follows: PATCH oldfile [-ooutput -ppatchfile] where: oldfile is the old file to be patched. output is the output file, default oldfile.new patchfile is the patchfile supplied by Lattice, Inc. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: : 14 :::::::::::::::::::::: 68881 Notes ::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: M68881 The code generated for the 68881 (-f8 option) runs differently than than standard code in the following ways: 1) Converting doubles to longs does not truncate the decimal places as with standard code. Instead it rounds off. 2) The recommended procedure for calling trig functions is to use the header file M68881.h. This code will run much faster than the routines in the lcm881.lib library. 3) The trig functions do not return the proper value if a error occurs, ie acos(1.1). 4) There is a known bug in placing a constant into a single precision float in modules compiled with -f8. :::::::::::::::::::END OF FILE:::::::::::::::::::::::::::::::::::::::::::