![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Machine Code Insertions Rational Software Corporation provides the package Machine_Code described in Section 13.8 of the Ada Language Reference Manual. See the Ada83 and Ada 95 LRM for more information.
The Ada RM specifies the format of a machine code insertion procedure but leaves the actual implementation to the compiler vendor.
Machine code insertions provide, from within the Ada language, low-level access to the processor that is normally available only from an assembly language.
Use of machine code insertions is necessary in programs that need to reference the hardware directly. They are also useful for optimizing time-critical sections beyond the capabilities of the compiler.
Apex machine code insertions provide features such as the X'Ref attribute, a full range of addressing modes and parameters that enable the programmer to integrate the machine code into surrounding code with minimal effort.
Machine code insertions are, however, a non-portable, processor-dependent feature of the language. The compiler cannot perform certain types of error checks that are performed for normal procedures (such as the enforcement of strong-typing). Machine code insertions should be used with discretion and only where absolutely necessary.
Machine code insertion support is provided for all Apex platforms.
The general structure of machine code insertions is the same for each architecture family. A separate section describes the target-dependent details for each architecture family.
- Machine Code Insertions Overview
- Machine Code Insertions - RS/6000
- Machine Code Insertions - Sun SPARC
- Machine Code Insertions - SGI MIPS
- Machine Code Insertions - HP-PA
- Machine Code Insertions - PowerPC
- Machine Code Insertions - Alpha Architecture
- Machine Code Insertions - Intel Architecture
- Machine Code Insertions - M68k Family
- Machine Code Insertions - RH-32
Machine Code Insertions OverviewPackage Machine_Code
The package Machine_Code defines the types and constants used in machine code insertions.
The specification of package Machine_Code can be found in an appropriate view of the lrm.ss subsystem:
$APEX_BASE/ada/lrm.ss/<view>
The view name is constructed from the host OS or target variant, Ada variant, and the product version:
Native: ${APEX_ARCH_OS}.ada{83|95}.${APEX_PROD_VERSION}.rel Embedded: <target-variant>.ada{83|95}.${APEX_PROD_VERSION}.rel
The "$APEX_*" meta-names are set when Apex is invoked. Their values can be displayed using Tools > Session > Environment command.
The name of the file depends on the Ada variant:
Ada83 machine_code.1.ada
Ada95 system.machine_code.1.ada
For Ada95, the file machine_code.1.ada provides a rename of the package System.Machine_Code. This allows existing "with" and "use" clauses of Machine_Code in Ada83 source to compile without modification.
Machine Code Procedures
A machine code procedure is restricted to the following, as imposed by the Ada Language Reference manual:
- Machine code insertions are only allowed within a procedure body.
- The only declarations that can occur in the declarative section of a machine code procedure are use clauses.
- The only statements that can occur in the body of a machine code procedure are code-statements (labeled or not).
- A machine code procedure cannot have an exception handler.
- Comments and pragmas are allowed as usual.
procedure identifier [formal_part] is {use_clause} begin {label} code_statement {{label} code_statement} end [identifier];
The body of a machine code procedure must be within the context of a with clause that names the package Machine_Code.
Even though this is only a limited form of a subprogram body, it can still be used wherever a subprogram body is allowed, even as a generic or as a separate body. There are no restrictions on the parameter profile. As with other subprograms, pragma Inline can also be applied.
Under most circumstances a machine code procedure should have a single exit point--the end of the procedure. It should not have an explicit return instruction as this would prevent the compiler-generated epilogue code from executing.
The only exception to this would be a non-inlined procedure written with Pragma Implicit_Code(Off).
Code Statements
A code-statement is used to specify the machine instruction and any operands needed by the instruction. The form of this construct is:
type_mark'record_aggregate;
The Type_Mark must be a record type defined in the predefined package Machine_Code. All the usual rules apply in forming an aggregate and all type checking is enforced. For single components, the aggregate must use named notation (cf. RM 4.3(4)).
Package Machine_Code provides the variant record types Code_{0..n}, Data_1, Data_N, Call_0, Call_1 and Call_N.
The Code_n types have a variant of the type Opcode and n components of type Operand.
Code_0'(op => Nop); Code_3'(And_Op, R1, R2, R3);
The Data_n types have a variant of the type Size. Data_1 has one operand component, while Data_N has a component that has a variable number of Operands.
Data_1'(Byte, Immed(Ascii.Lf); Data_N'(Word, (-1, +0, +1));
The Call_n types take a subprogram reference of the type Operand and n components of type Operand. while Call_N has a component that has variable number of Operands.
The types Opcode, Operand and Size are declared in the package Machine_Code.
Opcodes
Type Opcode is an enumeration type declared in the package Machine_Code. The type defines all of the supported instructions for an architecture family.
Instructions are named by their standard mnemonics, except where these would conflict with an Ada reserved word (e.g. Abs, And, Or, Xor). In these cases the mnemonic is suffixed with "_Op". In addition, any instruction qualifiers or completers may also be appended to the base mnemonic using an underscore, so that the enumeration is a legal Ada identifier.
Operands
Type Operand is a private type declared in the package Machine_Code.
The package provides constants of the type Operand and functions that return values of type Operand. The Apex-defined attribute X'Ref can be applied to most Ada objects and denotes a value of type Operand.
All the supported registers for an architecture family are provided as operand constants.
Intrinsic functions that return type Operand are provided to combine operands and expressions to form more complex addressing modes.
All arguments to machine code functions must be one of the following:
- static expression (cf. RM 4.9)
- type conversion (the expression Operand must be a static expression)
- string literal
- representation attribute (cf. RM 13.7.2)
- X'Ref attribute
- entity defined in the package Machine_Code
Ada Entities as Operands
It is sometimes necessary to reference Ada constants and variables from within a machine code procedure. It is very tedious and error-prone for a programmer to attempt to calculate these references by hand. Apex provides the attribute X'Ref, which generates a reference to the entity X. The definition is similar to the attribute X'Address.
The value of this attribute is of type Operand defined in the package Machine_Code. It is only valid within the context of a machine code procedure.
For a prefix X that denotes an object, X'Ref yields a reference to the first storage unit associated with X.
For a subprogram X'ref yields a reference suitable for use in a subroutine linkage instruction. The value may refer to a subprogram descriptor, or to the text address of first instruction of the subprogram. In the former case, a intrinsic function is provided to reference the text address, if necessary.
For a label, X'Ref yields a reference to the text address of the statement associated with the label, suitable for use in a branching instruction. The value depends on the context in which the label is referenced, but typically it is a PC-relative offset.
For a constant object with a static expression the value refers to the value of the static expression.
For an enumeration literal the value refers to the integer code associated with the enumeration literal, which is the same as its position number, unless a representation clause is given for the enumeration type.
Const'Ref and Enum'ref should only be used as operands to instructions where an immediate value would be appropriate.
In many cases, using X'Ref will cause more than a single instruction to be generated. This may be because additional code is required to form the address of the referenced object. For example:
Code_2'(STORE, R3, Record1.Field2'Ref);
In this case additional code may be inserted to load a base register with address of Record1.
The context in which an X'Ref attribute is used may affect the amount additional code needed. For example:
Code_2'(LOAD, R4, Arg1'Ref); -- Arg1 passed in a register Code_3'(ADD , Src'Ref, R4, Dst'Ref); -- Operates on 3 registers
Because the LOAD instruction requires a memory operand, but Arg1 is bound to a register, the compiler must generate code to bind the Arg1 to a stack location in order for the LOAD operation to work. In this case, because we know Arg1 is in a register, it would be more efficient to use a register-to-register move instruction instead of LOAD, or to reference Arg1 directly in the ADD instruction.
Similarly, because the ADD instruction requires three register operands, the compiler may need to allocate temporary registers and generate additional code to load Src'Ref into a register and to store the result register into Dst'Ref.
Program Control
Operands are used to control the flow of execution with instructions such as branching and subroutine linkage. Labels and subroutine names can be used in conjunction with the X'Ref attribute to form destinations for these instructions
Code_2'(BRANCH_F, Reg, Label'Ref); -- Branch to Label, if Reg = 0 Code_2'(JSR, Ra, Subp'Ref); -- Call subroutine
On architectures that have delay slots, it is not necessary (or possible) to explicitly fill these delay slots when writing Machine Code. At higher levels of optimization, the code generator will attempt to reorganize the instructions to minimize the number of delays. When NOPs are required, the code generator will insert them.
Call Statements
A general form of a subroutine call can be made from within a machine code procedure by using a Call statement. Three formats are provided, Call_0, Call_1 and Call_N to handle the various argument lists needed by a subroutine. The first argument to the call statement is the name of the subroutine suffixed by `Ref to yield a value of type Operand. All subsequent arguments, if any, must also be of type Operand. The number of arguments and their type compatibility are checked.
Warning: The current limitation on the arguments is that they must be of the form Name'Ref. All expressions must be assigned to a named entity before the call.
CALL_0'(SUBP => P'REF);
CALL_1'(P'REF, ARG'REF);
CALL_N'(P'REF, (ARG1'REF, ..., ARGn'REF));If P is a procedure, the following Ada procedure calls are equivalent to the statements shown above.
P; P(ARG); P(ARG1, ..., ARGn);
If P is function, the forms are similar but the result is not assigned.
If P is overloaded, an error is generated when using `Ref. The subroutine should be renamed to provide a distinct name.
The effect is to marshall the arguments (if any) into parameter registers or possibly onto the stack, make the call, copy the out parameters (if any) and adjust the sp (if needed). The return value of a function is not copied.
As with any ordinary procedure call, the call to P may be expanded inline, either by the compiler (at higher optimization levels) or at the programmer's request by applying one of the Inline Pragmas to the declaration of P. To avoid auto-inlining of P use Pragma Inline_Never(P), following the declaration of P, since this may not always be possible it may be necessary to program the call explicitly, rather than using a Call statement.
Data Statements
Variables visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute. In some cases, however, it may be necessary to intermix data and generated code. The Data_1 code-statement is used to place a single data item in the code, while the Data_N code-statement is used for multiple data items.
Data_1'(Size, Operand); Data_N'(Size, (Operand1, Operand2..));
Where Size is an enumeration literal of type Size defined in package Machine_Code. The size may be further restricted by the type of operand. For example, an X'Ref to an absolute address must use the size corresponding to the size of an address.
The supported operands for local data may vary slightly on each platform, but typically include the following:
- immediate
- absolute
- external symbol
- label reference
- label difference
- subprogram reference
Immediate and label reference are supported for all platforms.
DATA_1'(WORD, +16#EF_FFF0#); DATA_1'(WORD, IMMED(ASCII.LF)); DATA_N'(WORD, (LABEL1'REF, LABEL2'REF, LABEL3'REF));
Normally, Data statements should be guarded by a branch or return instruction, so that they are unreachable, otherwise, the processor will attempt to execute the data as if it were code. This will usually result in a hardware exception (e.g. an illegal instruction trap). But occasionally it can be useful to use Data to encode opcodes that are otherwise unavailable in machine code.
Pragmas
There are several pragmas that directly affect the code generated for machine code procedures.
- Pragma Inline
- Pragma Inline_Only
- Pragma Inline_Never
- Pragma Implicit_Code(Off)
- Pragma Optimize_Code
- Pragma Suppress
Pragma Inline
Pragma Inline provides a hint to the compiler that a machine_code routine may be inlined at the compiler's discretion. Auto-inlining occurs at higher levels of optimization, but machine_code routines are never candidates for auto-lining, unless explicitly marked with pragma Inline.
It is possible to write machine code that works correctly when called, but does not work correctly when inlined. For example, if the routine directly referenced a parameter register instead of using `Ref. Inline routines must always use `Ref to reference parameters.
For this reason, we recommend that pragma Inline not be used for machine code routines. Instead, we recommend that machine_code routines should be designed as callable routines, or as inline_only routines.
Pragma Inline_Only
Pragma Inline_Only informs the compiler that the routine must always be inline. Note that no callable body for the routine will be generated.
Pragma Inline_Never
Pragma Inline_Never informs the compiler that the routine must never be inlined. This is now redundant for machine_code routines because the policy is not to inline a machine_code routine unless specifically requested.
On the other hand, it may be necessary to apply Pragma Inline_Never to routines called from machine_code using a Call statement (Call_0, Call_1, Call_n), to prevent auto-inlining of those routines at higher optimization levels.
Pragma Implicit_Code(Off)
This pragma controls the generation of implicit code. Implicit code is code generated for procedure entry and exit to support the calling conventions used by the compiler. This does not include the return instruction, which is always generated unless the routine is inlined.
This means the compiler will not generate any code to save/restore non-volatile registers, or to create/destroy a call frame. It will not allocate any space on the stack for temporaries, and will not generate any Storage_Check for the routine.
Pragma Implicit_Code(Off) should only be used when the programmer requires complete control of the call frame. It should not be used as an optimization.
When it is used, the programmer is responsible for ensuring that the machine_code routine obeys all the linkage conventions required by the routines that will call it. For example, the programmer must explicitly save and restore any registers that the routine uses, if the caller requires them to be preserved across the call.
Implicit code also includes any additional code generated due to the use of the X'Ref attribute (such as code to load a base register).
This implicit code is always generated for a X'Ref attribute which requires it even when Implicit_Code(Off) is specified. A warning message is generated in this case. However, this warning only occurs when additional code is generated for a specific reference (e.g. loading a base register), and not when prologue code is also required for the reference. That is, the code generated for an X'Ref attribute may depend implicitly on prologue code (e.g. to establish a global pointer, to create a stack temporary, or to save a non-volatile register for temporary use), and hence the reference may be invalid, but the compiler will not issue a warning.
For this reason great care must be exercised when using the X'Ref attribute with pragma Implicit_Code(Off). In particular, the following should be avoided:
- X'Ref to parameters passed on the stack.
Since there is no frame, the compiler-generated references would be incorrect (e.g. relative to a frame-point that has not been established).
A routine could, of course, directly access parameters passed on the stack at known (constant) offsets from the SP, or from the FP, if it is explicitly established by the programmer.
- X'Ref to a register parameter in a memory context.
This would force to the compiler to bind the parameter to a memory location on the procedures stack. This binding occurs as part of the routine prologue which is omitted with Implicit_Code(Off), hence the reference would be incorrect.
- X'Ref to objects declared in an enclosing procedure.
Because there is no frame, references to up-level objects would be incorrect.
- X'Ref to library level, or external objects on architectures that require a global offset pointer, unless the global offset pointer is explicitly established, or known to be valid.
In general, it is recommended that Implicit_Code(Off) only be used in routines with no parameters, or where parameters are passed in registers, and in those cases the parameters could be referenced directly by register name, rather than with X'Ref.
Implicit_Code(Off) should not be used on machine code routines that are inlined. Inlined routines do not include entry/exit code, and require that parameters be referenced with `Ref.
If the Implicit_Code(Off) pragma is used, then the runtime it has no knowledge of the stack contents for that frame, therefore the runtime cannot unwind that frame. The result of this is that machine code routines using Implicit_Code(Off), do not propagate to the caller exceptions raised by machine code instructions or by any routines called by these routines.
When used, this pragma should be placed in the declarative section of the machine code procedure.
Pragma Optimize_Code
This pragma allows the programmer to specify whether the compiler should attempt to optimize through the machine code insertions. When pragma Optimize_Code(OFF) is specified most optimizations on machine_code will be suppressed. Some code transformations may still occur on some architectures (e.g. instruction scheduling and the filling of delay slots).
When used, this pragma should be placed in the declarative section of the machine code.
Pragma Suppress
This pragma has its normal effect of suppressing the generation of runtime checks. Runtime checks may be generated to perform an elaboration check at the start of the procedure and to perform constraint checks on objects accessed using X'Ref. These checks only ensure that the reference is valid; they do not check whether the value assigned is valid. For example, the code-statement
Code_2'(STORE, R0, X.All'Ref); -- Store R0 at X
would generate an access check to ensure that X is not null but would not generate a check to ensure that the value moved to the referenced location was in the range of the type of X.All.
Debugging Machine Code
The Apex debugger supports source-level debugging of machine code insertions. Breakpoints can be set at code-statements just like any other statements.
Register values can be determined using the Windows > Registers command from the GUI or the reg command in line mode or through the Command Pane.
Register values can also be displayed by preceding the register name with a dollar sign and using either the p command or a raw memory display command (e.g. $reg:<size><fmt>).
The Disassembly button (GUI) or the li and wi instructions (line-mode and Command Pane) can be used to disassemble the generated code.
There are some potential problems with debugging machine code.
A machine code routine may not have a standard frame or valid procedure descriptor (c.f. pragma Implicit_Code). This may prevent the debugger from correctly unwinding the call stack, because it is unable locate the return address, or properly restore the context of the calling routine.
The debugger will attempt to disassemble local Data statements as instructions, producing meaningless results, or worse. On machines with variable length instructions (e.g. X86, M68k) Data statements may disrupt the disassembly of the rest of the routine.
Machine Code Insertions - RS/6000This section covers the details of machine code insertions for the IBM RS/6000. Be sure to read the Machine Code Insertions Overview before proceeding with this section.
Opcodes
Type Opcode is an enumeration type declared in the package Machine_Code. The type provides all of the supported instructions for the RISC System/6000.
Instructions are named by their standard mnemonics, except where these would conflict with an Ada reserved word (e.g. Abs, And, Or, Xor). In those cases the mnemonic is suffixed with "_op". Instructions that set the OE bit, are appended by "_o". Instructions that set the Rc bit, are appended by "_r". Instructions that set both the OE and Rc bit are appended by "_o_r".
Operands and Addressing Modes
Operands
Type Operand is a private type declared in the package Machine_Code. The package also provides constants of the type Operand and functions that return values of type Operand. The Apex-defined attribute X'Ref can be applied to most Ada objects and denotes a value of type Operand.
Each of the supported RISC System/6000 registers are provided as operand constants.
Each of the supported RISC System/6000 Addressing modes are made available by functions that denote values of type Operand. The following table summarizes the available addressing modes and the functions used to support them.
Examples:
CODE_2'(L, R0, BASE(R1+4)); CODE_2'(L, R0, BASE(R1-16#40000#)); CODE_2'(L, R0, LABEL'REF); CODE_2'(CAL, R0, SUBP'REF); CODE_2'(CAL, R0, CODE(SUBP'REF)); CODE_2'(L, R0, EXT("_END")); CODE_2'(L, R0, EXT("_END", 4));
All arguments to machine code functions must be one of the following:
- static expression (cf. RM 4.9)
- type conversion (the expression Operand must be a static expression)
- string literal
- representation attribute (cf. RM 13.7.2)
- X'Ref attribute
- entity defined in the package Machine_Code
Special Purpose Registers
Package Machine_Code defines a subset of special-purpose as operand constants. All other special-purpose registers can be referenced using the Spr() function and an Spr_Id value (0..1023).
Ada Entities as Operands
Objects visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute.
In many cases, using X'Ref will cause more than a single instruction to be generated to form the address of the referenced object.
Labels
When the X'Ref attribute is applied a label it yields a reference to the text address of the statement associated with the label. The addressing mode generated depends on the instruction.
Code_2'(Cal, R2, Label'Ref); -- Address of LABEL (absolute) Code_1'(Beq, Label'Ref); -- Branch to LABEL (branch displacement)
Subprograms
When the X'Ref attribute is applied to subprograms, the reference denotes the subprogram descriptor. The Machine Code intrinsic function Code() yields the address that references the text of the subprogram.
Code_2'(Cal, R2, Subp'Ref); -- Address of descriptor
Code_2'(Cal, R2, Code(Subp'Ref)); -- Address of text
Extended Addressing
Extended addressing allows a displacement exceeding the range of -32,768 to 32,767 for the following instructions:
Table 7 Extended Addressing
cal lfdu lhz stb stfsu
l lfs lhzu stbu sth
lbz lfsu lm stfd sthu
lbzu lha lu stfdu stm
lfd lhau st stfs stu
In most cases involving X'Ref, implicit code is generated to form the address.
Floating-Point
Floating-point operations are invoked directly by machine instructions which access the RISC System/6000 FPU. The package Machine_Code provides the opcodes and floating-point registers so that the FPU can be referenced directly from machine-code insertions.
The operands consist of the floating-point registers Fr0-Fr31, literals and X'Ref (where applicable).
Code_3'(Fa, Fr0, Fr1, Fr2);
In addition, an immediate can be used in LFS and LFD instructions.
Code_2'(Lfs, Fr0, +3.14); Code_2'(Lfd, Fr0, Immed(1.0/3.0));
Programming Examples
Start-Up Routine
The following example illustrates a hypothetical start-up routine for an Ada program. Its function is to call an initialization routine, and then invoke each entry in the elaboration table that is constructed by the linker.
with Machine_Code; procedure Start is use Machine_Code; pragma Implicit_Code (Off); begin -- Perform NLS initialization. Code_2'(Lil, R3, +0); Code_1'(Bl, Ext ("._NLinit")); Code_3'(Cror, +15, +15, +15); -- Initialize errno. Code_2'(Lil, R3, +0); Code_2'(St, R3, Ext ("errno")); -- -- Index through the elaboration table setup by a.ld and -- elaborate each package in the program. -- Code_2'(Cal, R13, Ext ("ELABORATION_TABLE")); <<Elab>> Code_2'(L, R14, Base (R13)); -- Load r14 with DESCR entry. Code_2'(L, R0, Base (R14)); -- Load r0 with routine entry. Code_2'(Mtspr, Ctr, R0); -- Set target for branch in ctr reg. Code_2'(L, R2, Base (R14 + 4)); -- Get address of new TOC. Code_0'(Op => Bctrl); -- Invoke elab code. Code_3'(Ai, R13, R13, +4); -- Increment elab table pointer. Code_1'(B, Elab'Ref); -- Loop to continue elaboration. end Start;
Machine Code Insertions - Sun SPARCThis section covers the details of machine code insertions for the SPARC Architecture Family. Be sure to read the Machine Code Insertions Overview before proceeding with this section.
Opcodes
Type Opcode is an enumeration type declared in package Machine_Code. This type provides all of the supported instructions for the SPARC, including the additional pseudo-instructions described later. Instructions are named by their standard mnemonics, except where those mnemonics that would conflict with an Ada reserved word (e.g. Abs, And, Or, Xor). In those cases the mnemonic is suffixed with "_Op".
Pseudo-Instructions
A set of pseudo-instructions are supported which are mapped to hardware instructions, as the following table describes.
Sparc-V9 Pseudo-Instructions
SPARC-V9 extends the SPARC architecture to 64-bits. In order to aid the writing of machine code that is easily portable between 32-bit and 64-bit programs, or between PIC and non-PIC models, APEX Ada provides the LDGP and LEA pseudo-instructions in machine code.
LDGP - Load Global Pointer
In SPARC PIC mode (32-bit and 64-bit) the local register%l7 is used as the Global Offset Table pointer, also called the "GP". The LDGP pseudo-instruction allows the machine code programmer to insert code to establish a valid GP, if required.
- In PIC mode (32-bit and 64-bit), LDGP load the GP into %l7. It also makes use of %o7, and in 64-bit mode it also uses %g5, the scratch register in 64-bit mode.
- When LDGP is used with pragma Implicit_Code(off), it is the programmer's responsibility to save and restore %o7 and %l7, either explicitly or by establishing a new register window.
- When pragma Implicit_Code(Off) is not used the compiler automatically establishes a new register window, so there is no need to save %o7 and %l7. In addition, the compiler will detect usage of LEA in PIC mode, and automatically insert code in the prologue to load the GP into%l7, so there is no need to explicitly use LDGP in this case.
- In non-PIC mode (32-bit and 64-bit) LDGP is ignored
LEA - Load Effective Address
The LEA pseudo-instruction provides a convenient way of loading the address of an object into a register, without have to know whether the code will be compiled for 32-bit or 64-bit modes, or for PIC or non-PIC modes.
- In 64-bit mode, loading a 64-bit value (address or constant) into a register requires using a scratch register. We use %g5 for this purpose, since it is a scratch register as per the 64-bit ABI. In the 32-bit ABI %g5 is a system reserved register and is not available for general use.
The table below shows the instruction sequences generated for LDGP and LEA under each programming model.
Operands and Addressing Modes
Type Operand is a private type declared in the package Machine_Code. This package also provides constants of type Operand and functions that return values of type Operand. The Apex-defined attribute X'Ref can be applied to most Ada objects and denotes a value of type Operand.
All SPARC registers are provided as operand constants.
All SPARC addressing modes are made available by functions that denote values of type Operand. The following table summarizes the available addressing modes and the functions used to support them.
-----------------------------------------------------------------------------
A Regaddr and Address are formed as follows:
regaddr : reg | reg + reg address : regaddr | reg + const13 | reg - const13 | const13
A Const13 is a signed constant which fits in 13 bits:
const13 : IMMED(value) | +value | -value | LO(value [, disp])
The operators HI and LO accept the following kinds of arguments:
- string (denoting an external name)
- integer expression
The operators Hi, Lo and Ext, allow an additional displacement to be added to an external symbol.
The operator Hi is only allowed in a sethi instruction. The operator Ext is only allowed in a call and set instruction.
Code_2'(Ld, Addr (16#FFF#), G2); Code_2'(Ld, Addr (G3), G4); Code_2'(Lda, Addr (G3, 3), G4); Code_2'(Ld, Addr (G1 + G3), G2); Code_2'(Ld, Addr (G1 + 16#FFF#), G2); Code_2'(Ld, Addr (G1 + Lo ("_main")), G2); Code_2'(Sethi, +16#3FFFFF#, G1); Code_2'(Sethi, Hi_Mod32 (16#FFFFFC01#), G1); Code_3'(Or_Op, G1, Lo_Mod32 (16#FFFFFC01#), G2); Code_1'(Call, Ext ("_main")); Code_2'(Set, Ext ("_main"), R1); Code_1'(Rett, G1 + G2);
SPARC-V9 extends the SPARC architecture to 64-bits. The operators Hh, Hm, Lm, are analogous to Hi and Lo, but operate on 64-bit operands. The suggested assembly syntax for SPARC-V9 defines the SETX synthetic instruction to load a 64-bit constant. Currently Apex Ada does not support this form. To load a 64-bit value a temporary register is needed, and in the most general case requires six instructions.
Code_2'(Sethi, Hh(value), G5); Code_3'(Or_op, G5, Hm(value), G5); Code_3'(Sllx, G5, +32, G5); Code_2'(Sethi, Lm(value), L2); Code_3'(Or_op, L2, G5, L2); Code_3'(Or_op, L2, Lo(value), L2);All arguments to machine code functions must be one of the following:
- static expression
- type conversion (the expression operand must be a static expression)
- string literal
- representation attribute
- X'Ref attribute
- entity defined in the package Machine_Code
Ada Entities as Operands
Objects visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute. In many cases, using X'Ref will cause more than a single instruction to be generated.
Labels
When the X'Ref attribute is applied a label it yields a reference to
the text address of the statement associated with the label. The
addressing mode generated depends on the instruction.
Code_2'(Set, Label'Ref, R1); -- Address of Label (absolute) Code_1'(Ba, Label'Ref); -- Branch to LABEL (branch displacement)Subprograms
When the X'Ref attribute is applied to a subprogram, the reference denotes the address of the subprogram.
CODE_1'(CALL, SUBP'REF);
Programming Examples
In the ensuing discussions, we use the SPARC 32-bit model, unless otherwise specified.
Program Control
Labels and subroutine names are used in conjunction with the X'Ref attribute to form destinations for branch and subroutine linkage instructions.
The following example illustrates a typical startup routine for an Ada program. Its function is to call an initialization routine, elaborate the library units, call the main program and call an exit routine.
with Machine_Code; procedure Start is use Machine_Code; pragma Implicit_Code (Off); begin -- Set stack limit in %G4 Code_2'(Sethi, Hi ("USER_STACK_SIZE"), G1); Code_2'(Ld, Addr (G1 + Lo ("USER_STACK_SIZE")), G4); Code_3'(Sub, Sp, G4, G4); -- Save a pointer to the args and environment, which starts -- at %sp+64 Code_3'(Add, Sp, +64, G3); Code_2'(Sethi, Hi ("__u_mainp"), G1); Code_2'(St, G3, Addr (G1 + Lo ("__u_mainp"))); Code_2'(Ld, Addr (G3), G1); -- argc Code_3'(Sll, G1, +2, G1); Code_2'(Inc, +8, G1); Code_3'(Add, G1, G3, G1); Code_2'(Sethi, Hi ("_environ"), G2); Code_2'(St, G1, Addr (G2 + Lo ("_environ"))); -- Call the package elaboration routines in ELABORATION_TABLE -- The address of __stop is the end of the call stack. Code_2'(Set, Stop_Lab'Ref, G1); Code_2'(Sethi, Hi ("__stop"), G2); Code_2'(St, G1, Addr (G2 + Lo ("__stop"))); Code_2'(Set, Ext ("ELABORATION_TABLE"), L0); <<Elab>> Code_2'(Ld, Addr (L0), L1); Code_2'(Cmp, L1, +0); Code_1'(Be, Done'Ref); Code_0'(Op => Nop); Code_1'(Call, L1); Code_0'(Op => Nop); <<Stop_Lab>> Code_2'(Inc, +4, L0); Code_1'(Ba, Elab'Ref); Code_0'(Op => Nop); <<Done>> Code_1'(Clr, O0); Code_1'(Call, Ext ("__exit")); Code_0'(Op => Nop); end Start;Note the use of NOP instructions following branch and call instructions.This is necessary to fill the branch delay slots for these instructions.
Jump Table via Absolute Addresses
A jump table can be constructed by building a table of absolute addresses. The table is built by using the data statement, where the operands consist of label references to the selected entry points. An absolute address mode specifying the physical address can also be used. The following program fragment illustrates the technique.
with Machine_Code; procedure Example (Index : Integer) is use Machine_Code; begin -- Assume INDEX has the values 0, 1, ..., n Code_3'(Sll, Index'Ref, +2, O0); Code_2'(Set, Table'Ref, O1); Code_2'(Ld, Addr (O0 + O1), O0); Code_1'(Jmp, O0); Code_0'(Op => Nop); <<Table>> Data_1'(Word, L0'Ref); Data_1'(Word, L1'Ref); -- ... Data_1'(Word, Ln'Ref); <<L0>> Code_1'(Ba, Done'Ref); Code_0'(Op => Nop); <<L1>> Code_1'(Ba, Done'Ref); Code_0'(Op => Nop); -- ... <<Ln>> Code_1'(Ba, Done'Ref); Code_0'(Op => Nop); <<Done>> Code_0'(Op => Nop); -- Target statement for label -- Compiler-generated epilogue code here end Example;
Each code segment in the table must branch to the end of the routine so that the compiler-generated epilogue code for the routine is executed.
Replacing the branch with a return instruction would not be correct, unless the routine was written with Pragma Implicit_Code(Off). In which case it would be entirely up to the programmer to handle the subroutine linkage.
Machine Code Insertions - SGI MIPSThis section covers the details of machine code insertions for the SGI MIPS processor. Be sure to read theMachine Code Insertions Overview before proceeding with this section.
Opcodes
Type Opcode is an enumeration type declared in the package Machine_Code. The type defines all of supported the instructions for the MIPS, including additional pseudo-instructions described later.
Instructions are named by their standard mnemonics, except where these would conflict with an Ada reserved word (e.g. Abs, And, Or, Xor). In those cases the mnemonic is suffixed with "_Op".
Pseudo Instructions
A subset of the MIPS defined pseudo-ops, also called synthetic instructions, are supported. Following is a list of these instructions with the MIPS name in parenthesis when the names are not identical.
abs_op (abs), div, divu, la, li, move, nop, seq, sne, ulh, ulhu, ulw, ush, usw.
Operands and Addressing Modes
The type Operand is a private type declared in the package Machine_Code. The package also provides constants of the type Operand and functions that return values of type Operand. The Apex-defined attribute X'Ref can be applied to most Ada objects and denotes a value of type Operand.
All the supported MIPS registers are provided as Operand constants.
All the supported MIPS addressing modes are made available by functions that denote values of type Operand. Table 29 summarizes the available addressing modes and the functions used to support them.
Examples
Code_2'(Lw, R2, Disp (R1, +4)); Code_2'(Lw, V0, Disp (R1, -16#40000#)); Code_2'(Lw, T3, Disp (R0, +4)); Code_2'(Lw, S0, Disp (R0, -16#7FFFE#)); Code_2'(Lw, S5, Label'Ref); Code_2'(Lw, S0, Label'Ref); Code_1'(Jal, Subp'Ref); Code_2'(Lw, T3, Ext ("_END"));
All arguments to machine code functions must be one of the following:
- Static expression (cf. RM 4.9)
- Type conversion (the expression operand must be a static expression)
- String literal
- Representation attributes (cf. RM 13.7.2)
- X'Ref attribute
- Entity defined in the package Machine_Code
Ada Entities as Operands
Objects visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute. In many cases, using X'Ref will cause more than a single instruction to be generated to form the address of the referenced object.
Labels
When the X'Ref attribute is applied a label it yields a reference to the text address of the statement associated with the label. The addressing mode generated depends on the instruction.
Code_1'(Jal, Label'Rer); -- Address of Label (absolute) Code_3'(Beq, R0, R0, Label'Ref); -- branch to Label (branch displacemnt)Subprograms
When the X'Ref attribute is applied to a subprogram, the reference denotes the address of the subprogram.
Code_1'(Jal, Subp'Ref);
Extended Addressing
Extended addressing allows a displacement exceeding the range of -32,768 to 32,767 for the following instructions:
lw lh lhu lb
lbu sw sh sb
lwl lwr swl swr
In most cases involving X'REF, implicit code is generated to form the address.
Floating-Point
Floating-point operations are invoked directly by machine instructions which access the MIPS floating point coprocessor. The Machine_Code package provides the opcodes and floating-point registers so that the coprocessor can be referenced directly from machine-code insertions.
Parameters
The operands consist of the floating-point registers f0-f31, general purpose registers, based addressing, literals and X'Ref (where applicable).
Code_3'(Sub_d, F0, F1, F2);
Note that for double-precision, a register pair (even-odd) is used. For single-precision, only even registers may be used. Only those instructions which load and store floating point registers may use odd-numbered registers.
In addition, an immediate can be used in LI_D and LI_S instructions. For example,
Code_2'(Li_d, F0, Immed(1.0)); Code_2'(Li_s, F0, Immed(3.14));
Programming Examples
Start-Up Routine
The following example illustrates a hypothetical start-up routine for an Ada program. Its function is to call an initialization routine, and then invoke each entry in the elaboration table that is constructed by the pre-linker.
with Machine_Code; procedure V_Start is pragma Implicit_Code (Off); pragma Optimize_Code (Off); use Machine_Code; begin -- Save gp to restore. Code_2'(Move, S2, Gp); -- Mark the frame pointer to indicate top of stack for the -- program. Code_2'(Li, Fp, +0); -- Initialize the Ada "new" memory allocation Code_2'(La, A0, Configuration_Table'Ref); Code_2'(La, T9, V_Usr_Conf_I.Aa_Init'Ref); Code_2'(Jalr, Ra, T9); Code_2'(Move, Gp, S2); -- save away the address of <<stop_lab>>. Its -- the top return address on the call stack. Its used to "stop" -- exception unwinding. Code_2'(La, T0, Stop_Lab'Ref); Code_2'(La, T1, V_Usr_Conf_I.Stop'Ref); Code_2'(Sw, T0, Disp (T1, +0)); -- Index through the elaboration table setup by the linker and -- elaborate each package in the program. Code_2'(La, S0, Ext ("ELABORATION_TABLE")); <<Elab>> Code_2'(Lw, T9, Disp (S0, +0)); -- Address from elaboration table Code_3'(Addiu, S0, S0, +4); -- Zero designates end-of-table Code_3'(Beq, T9, Zero, Elab_Done'Ref); -- The second to last entry is the main subprogram. -- It may a function returning its status in v0. -- For a main that is a function, the last entry is -- return_result() or ts_exit_result(). These subprograms -- expect the status to be in a0. The return status for the previous -- entry is saved in s1. Code_2'(Move, A0, S1); -- Pass previous status (if any) Code_2'(Jalr, Ra, T9); -- Invoke code for current elaboration entry. <<Stop_Lab>> Code_2'(Move, Gp, S2); -- Save (possible) return status Code_2'(Move, S1, V0); Code_3'(Beq, Zero, Zero, Elab'Ref); <<Elab_Done>> -- Exit the user program Code_2'(La, T9, V_Usr_Conf_I.Ada_Exit'Ref); Code_2'(Jalr, Ra, T9); -- No return back end V_Start;When writing in machine code, it is not possible to fill delay slots, nor is it necessary to add NOP instructions when they would normally be needed. The code generator will attempt to reorganize the instructions to minimize the number of delays. When NOPs are needed, the code generator will insert them.
In this case, since Pragma Optimize_Code(Off) is used, the code generator will not attempt to reorganize the code to fill delay slots. All delay slots will be filled with NOPs.
Jump Table via Addresses
A jump table can be constructed by building a table of addresses. The table is built by using the Data statement, where the operands consist of label references to the selected entry points.
The following program fragment illustrates the technique.
with Machine_Code; procedure Example (Index : Integer) is use Machine_Code; -- Assume Index has the values 0, 1, ..., n begin Code_2'(La, T0, Table'Ref); -- Load address of TABLE Code_3'(Sll, T1, Index'Ref, +2); -- Scale INDEX Code_3'(Addu, T0, T0, T1); -- Calculate TABLE address Code_2'(Lw, T1, Disp (T0)); -- Load address from TABLE Code_1'(Jr, T1); -- Branch to destination <<Table>> Data_1'(Word, L0'Ref); Data_1'(Word, L1'Ref); -- ... Data_1'(Word, Ln'Ref); <<L0>> -- Code for index 0 -- ... Code_3'(Beq, Zero, Zero, Done'Ref); <<L1>> -- Code for index 1 -- ... Code_3'(Beq, Zero, Zero, Done'Ref); -- <<L2..Ln - 1>> <<Ln>> -- Code for index n -- ... Code_0'(Op => Nop); -- Fall Through... <<Done>> Code_0'(Op => Nop); -- Target statement for label -- Compiler-generated epilogue code here end Example;Each code segment in the table must branch to the end of the routine so that the compiler-generated epilogue code for the routine is executed.
Replacing the branch with a return instruction would not be correct, unless the routine was written with Pragma Implicit_Code(Off). In which case it would be entirely up to the programmer to handle the subroutine linkage.
Machine Code Insertions - HP-PAThis section covers the details of machine code insertions for the HP-PA processor. Be sure to read the Machine Code Insertions Overview before proceeding with this section
Opcodes
Type Opcode is an enumeration type declared in the package Machine_Code. The type provides all of the supported instructions for the PA-RISC processor.
Instructions are named by their standard mnemonics, except where these would conflict with an Ada reserved word (e.g. And, Or, Xor). In those cases the mnemonic is suffixed with "_Op". The various completers are appended to the base mnemonic and separated by an underscore.
The following table maps assembly completers to alphanumeric counterparts that can legally comprise an identifier.
Table 11 Arithmetic/Logical Conditions
= eq <> ne
< lt >= ge
<= le > gt
<< ltu >>= geu
<<= leu >> gtu
false? f !?<= ogt
false sf > gt
? ngle ?> ugt
!<=> un !<= nle
= eq !?< oge
=T seq >= ge
?= ueq ?>= uge
!<> ngl !< nlt
!?>= olt !?= ogl
< lt <> gl
?< ult != ne
!>= nge !=T sne
!?> ole !? or
<= le <=> gle
?<= ule true? t
!> ngt true st
* x
Note: The 64-bit form of the above predicates have a `*' prefixed to them. Machine code instructions replace the `*' with an `x'. For example, `*=' will be `xeq' (`eq' is for `='), *<< will be `xltu' (`ltu' is for `<<`), etc.
COPR and SPOP instructions
For these instructions the SFU/UID and SOP opcode completers are specified as immediate values in the first and second operands respectively.
Code_2'(Copr_N, +0, +0); Code_4'(Spop3, +0, +0, r1, r2);
Operands and Addressing Modes
Type Operand is a private type declared in package Machine_Code. The package also provides constants of type Operand and functions that return values of type Operand. The Apex-defined attribute X'Ref can be applied to most Ada objects and denotes a value of type Operand.
All the supported PA-RISC registers are provided as operand constants.
All the supported PA-RISC addressing modes are made available by functions that denote values of type Operand.
The following table summarizes the available addressing modes and the functions used to implement them.
Examples
CODE_2'(LDO, ADDR(R1+4), R1); CODE_2'(LDW, ADDR(3, R1-16#40000#), R1); CODE_2'(LDO, ADDR(+16#7FFFE#), R1); CODE_2'(LDO, LABEL'REF, R1); CODE_2'(LDO, SUBP'REF, R1); CODE_2'(LDO, SUBP_EXT("_exit"), R1); CODE_1'(BE, ADDR(SR4, R1-16#400#));
All arguments to code functions must be one of the following:
- static expression (Ada LRM 4.9)
- type conversion (the expression operand must be a static expression)
- string literal
- representation attributes (Ada LRM 13.7.2)
- X'Ref attribute
- entity defined in the package Machine_Code
Ada Entities as Operands
Objects visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute. In many cases, using X'Ref will cause more than a single instruction to be generated to form the address of the referenced object.
Labels
When the X'Ref attribute is applied to labels, the addressing mode generated is a branch displacement or evaluates to an address depending on the instruction.
For example, the following instruction uses a branch displacement.
Code_3'(Comb_Eq, R3, R4, Label'Ref); -- Branch to Label
The following instruction evaluates the address.
Code_2'(Ldo, Label'Ref, R3); -- Address of Label
Subprograms
When the X'Ref attribute is applied to subprograms, the reference denotes the machine code of the subprogram.
CODE_2'(LDO, SUBP'REF, R3);
The functions Ext and Subp_Ext are used to reference external names. The function Ext is used for data references. The function Subp_Ext is used for subprogram references.
CODE_2'(LDO, EXT("__DATA__", R3);
CODE_2'(LDO, SUBP_EXT("__SUBPROGRAM__", R3);
Extended Addressing
Extended addressing allows a displacement exceeding the range of -8192 to 8191 for the following instructions:
ldb ldh ldw
stb sth stw
ldwm stwm ldo
Extended addressing allows a displacement exceeding the range of -16 to 15 for the following instructions:
ldbs ldhs ldws ldwas ldcws ldsid stws stws_bc sths sths_bc stbs stbs_bc stwas stwas_bc stbys stbys_b stbys_bc stbys_b_bc stbys_e stbys_e_bc fldws fldds fstws fstds
In most cases involving X'Ref, implicit code is generated to form the extended address.
Floating-Point
Floating-point operations are invoked directly by machine instructions which access the PA-RISC FPU. Package Machine_Code provides the opcodes and floating-point registers so that the FPU can be referenced directly from machine code insertions.
The operands consist of the floating-point registers Fr0-Fr31, Fr0L-Fr31L, Fr0R-Fr31R, literals, and X'Ref (where applicable).
Code_3'(Fadd_Dbl, Fr4, Fr5, Fr6);
Programming Examples
Start-Up Routine
The following example illustrates a hypothetical start-up routine for an Ada program. Its function is to call an initialization routine, and then invoke each entry in the elaboration table that is constructed by the linker.
with Machine_Code; procedure Start is use Machine_Code; pragma Implicit_Code (Off); begin -- -- Index through the elaboration table setup by a.ld and -- elaborate each package in the program. -- Code_2'(Ldo, Ext ("ELABORATION_TABLE"), R3); <<Elab>> Code_2'(Ldwm, Addr (0, R3 + 4), R22); -- Load next entry and -- increment pointer. Code_3'(Combt_Eq_N, R22, R0, Elab_Done'Ref); -- End of list? Code_0'(Op => Nop); Code_1'(Ble, Addr (Sr4, R22)); -- Invoke elaboration code. Code_2'(Copy, Mrp, Rp); Code_1'(B_N, Elab'Ref); -- Loop to continue elaboration. Code_0'(Op => Nop); <<Elab_Done>> Code_0'(Op => Nop); -- Need target for label. end Start;
Machine Code Insertions - PowerPCThis section covers the details of machine code insertions for the PowerPC processor. Be sure to read the Machine Code Insertions Overview before proceeding with this section.
Opcodes
Type Opcode is an enumeration type declared in the package Machine_Code. The type provides all of the supported instructions for the PowerPC processors. The opcode mnemonics and operand structure are taken directly from the PowerPC User Manuals with the following modifications:
- The use of `.' in an opcode name is replaced by `_'.
- Opcodes which coincide with predefined Ada operators OR, And, Xor are replaced by Or_Op, And_Op, and Xor_Op.
- The memory instructions (load/store) are treated in machine code as two operand instructions since memory references where the second and third operands are combined into an addressing mode with one of the Machine_Code addressing mode functions.
Operands and Addressing Modes
All the supported PowerPC registers are provided as Operand constants (including all of the control registers). For example, the following code-statement moves the contents of R2 to R1.
Code_2'(Mv, R1, R2);
All the supported PowerPC addressing modes are made available by Machine_Code functions that denote values of type Operand. The following table summarizes the available addressing modes and the functions used to implement them.
Examples
Code_2'(Lwz, R9, Base(R2+56)); Code_2\Q(Lwz, R9, Ext("_H_T"));
All arguments to machine code functions must be one of the following:
- static expression (cf. RM 4.9)
- type conversion (the expression Operand must be a static expression)
- string literal
- representation attribute (cf. RM 13.7.2)
- X'Ref attribute
- entity defined in the package Machine_Code
Special Purpose Registers
Package Machine_Code defines a subset of special-purpose as operand constants. All other special-purpose registers can be referenced using the Spr() function and an Spr_Id value (0..1023). In addition, Package machine_code also defines some model-specific SPRs as named constants of type Spr_Id.
Ada Entities as Operands
Objects visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute. In many cases, using X'Ref will cause more than a single instruction to be generated to form the address of the referenced object.
Labels
When the X'Ref attribute is applied a label it yields a reference to the text address of the statement associated with the label. The addressing mode generated depends on the instruction.
Code_2'(La, R1, Label'Ref); -- Address of Label Code_1'(B, Label'Ref); -- Branch to Label
Subprograms
When the X'Ref attribute is applied to a subprogram, the reference denotes the address of the subprogram.
Code_1'(Bl, Code (V_Usr_Conf_I.Ada_Exit'Ref));
Floating-Point
Floating-point operations are invoked directly by machine instructions which access the PowerPC FPU. package Machine_Code provides the opcodes and floating-point registers so that the FPU can be referenced directly from machine-code insertions.
The operands consist of the floating-point registers Fr0-Fr31, literals and X'Ref (where applicable).
Code_3'(Fadd, FR0, FR1, FR2);
Programming Examples
Start-Up Routine
The following example illustrates a hypothetical start-up routine for an Ada program. Its function is to call an initialization routine, and then invoke each entry in the elaboration table that is constructed by the pre-linker.
with Machine_Code; procedure V_Start is use Machine_Code; pragma Implicit_Code (Off); -- Inputs: -- R1 = stack frame pointer -- R2 = TOC ptr begin -- Mark the frame pointer to indicate top of stack for the program. Code_2'(Lil, Fp, +0); -- back chain -- Initialize the Ada "new" memory allocation Code_2'(La, R3, Configuration_Table'Ref); Code_1'(Bl, Code (V_Usr_Conf_I.Aa_Init'Ref)); -- -- Save away the address of <<stop_lab>>. Its the top return -- addresson the call stack. Its used to "stop" exception -- unwinding. -- Code_2'(La, R0, Stop_Lab'Ref); Code_2'(Stw, R0, V_Usr_Conf_I.Stop'Ref); -- -- Index through the elaboration table setup by a.ld and elaborate -- each package in the program. -- Code_2'(La, R15, Ext ("ELABORATION_TABLE")); Code_2'(Lwz, R16, Base (R15)); -- Load r16 with elab subp <<Elab>> Code_3'(Cmpi, Cr0, R16, +0); -- exit when address = zero Code_2'(Beq, Cr0, End_Elab'Ref); Code_2'(Mtspr, Ctr, R16); -- Set target for branch in -- ctr reg. Code_0'(Op => Bctrl); -- Invoke elab code. <<Stop_Lab>> Code_2'(Lwzu, R16, Base (R15 + 4)); -- Increment elab table pointer Code_1'(B, Elab'Ref); -- Loop to continue elaboration <<End_Elab>> Code_2'(Lil, R3, +0); -- Exit status 0 Code_1'(Bl, Code (V_Usr_Conf_I.Ada_Exit'Ref)); end V_Start;
Machine Code Insertions - Alpha ArchitectureThis section covers the details of machine code insertions for the Alpha Architecture. Be sure to read the Machine Code Insertions Overview before proceeding with this section.
Opcodes
Type Opcode is an enumeration type declared in the package Machine_Code. The type defines all of the supported instructions for the Alpha architecture, including additional pseudo-instructions described later.
Instructions are named by their standard mnemonics, except where those mnemonics that would conflict with an Ada reserved word (e.g. Abs, And, Or, Xor). In those cases the mnemonic is suffixed with "_Op".
Operands and Addressing Modes
Type Operand is a private type declared in the package Machine_Code. The package also provides constants of the type Operand and functions that return values of type Operand. The Apex-defined attribute X'Ref can be applied to most Ada objects and denotes a value of type Operand.
All the supported ALPHA registers are provided as operand constants.
All the supported ALPHA addressing modes are made available by functions that denote values of type Operand. The following table summarizes the available addressing modes and the functions used to support them.
Examples
Code_2'(Ldl, T0, Label'Ref); -- Load long word at label Code_2'(La, T0, Label'Ref); -- Load address of label Code_2'(Ldl, T2, Base (T0)); Code_2'(Ldl, T1, Disp (T0, +8)); Code_2'(Ldl, T1, Disp (T0, -16#0F0#)); Code_2'(Ldl, T1, Disp (T0, Y)); -- Y is constant integer; Code_2'(Jsr, Ra, Sub1'Ref); Code_2'(La, Pv, Sub1'Ref); Code_2'(Jsr, Ra, Pv); Code_2'(Ldl, T2, Ext ("_A_test_mc")); Code_2'(Ldl, T2, Ext ("_A_test_mc", 4));
All arguments to machine code functions must be one of the following:
- static expression (Ada LRM 4.9)
- type conversion (the expression operand must be a static expression)
- string literal
- representation attributes (Ada LRM 13.7.2)
- X'Ref attribute
- entity defined in the package Machine_Code
Ada Entities as Operands
Objects visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute. In many cases, using X'Ref will cause more than a single instruction to be generated.
Labels
When the X'Ref attribute is applied a label it yields a reference to the text address of the statement associated with the label. The addressing mode generated depends on the instruction.
Code_2'(La, T11, Label'Ref); -- Address of Label (absolute) Code_2'(Br, R31, Label'Ref); -- Branch to Label (branch displacement)Subprograms
When the X'Ref attribute is applied to a subprogram, the reference denotes the address of the subprogram.
Code_2'(Jsr, Ra, Subp'Ref);
Extended Addressing
Extended addressing allows displacements up to 32-bits, for the following memory format instructions:
LDL STL LDF STF LDQ STQ LDG STG LDL_L STL_C LDS STS LDQ_L STQ_C LDT STT LDQ_U STQ_U
In most cases involving X'REF, implicit code is generated to form the address.
Floating-Point
An implementation of the Alpha Architecture may provide either VAX or IEEE floating-point operations, both, or none.
Floating-point operations are accessible via machine code insertions in the same way that integer operations are. The Machine_Code package provides the opcodes and operands for all the floating point operations described in the Alpha Architecture Manual.
Internally, the compiler uses IEEE floating-point types to represent Ada float types.
Valid operands consist of the floating-point registers (F0-F31), general purpose registers, literals and X'Ref, where applicable.
Consider the following example of floating-point operands:
with Machine_Code; procedure Flt is Pi : constant Float := 3.14; E : Float := 2.7; procedure Sub1 (Arg1 : Float) is use Machine_Code; begin -- Code_2'(Ldt, F1, Pi'Ref); -- (A) Illegal -- Code_2'(Ldit, F2, E'Ref); -- (B) Illegal -- Code_2'(Ldt, F3, Arg1'Ref); -- (C) Illegal Code_2'(Ldit, F1, Pi'Ref); -- (A1) F1 := value of Pi Code_2'(Ldt, F2, E'Ref); -- (B1) F2 := value of E Code_2'(Fmov, Arg1'Ref, F3); -- (C1) F3 := Arg1 Code_2'(Ldit, F0, Immed (3.14)); -- (D) F0 := 3.14 end Sub1; begin Sub1 (1.0); end Flt;Lines A, B, and C are all illegal.
Line A is illegal because Pi'ref is a constant float value, not a memory reference as required by LDT.
Line B is illegal because E'ref is a memory reference, not a constant float value as required by LDIT.
Line C is illegal because Arg1'ref is a register reference, not a memory reference as required by LDT.
Lines A1-C1 show legal code statements corresponding to lines A-B.
Line D shows another way to load an literal float value.
Pseudo-Instructions
Apex supports all the pseudo-operations (stylized code forms) described in the Alpha Architecture Reference Manual, although the names may vary slightly. For example, OR_OP is used instead of OR, because OR is an Ada reserved word. These instructions are typically mapped to a single machine instruction.
Apex also supports most of the synthetic pseudo-instructions described in the Assembly Language Programmer's Guide (http://tru64unix.compaq.com/faqs/publications/pub_page/PRG_DOCS.HTM). These instructions typically generate more than one machine instruction.
Apex also support additional pseudo-instructions which are described in the sections that follow.
ALIGN
code_1'(align, +N);
This instruction generates zero to three NOPs, so that the low-order N bits of the address of the machine_code instruction that follows will be zero.
Acceptable values of N are 0, 1, 2, 3, and 4. However, since machine_code instructions are naturally 4-byte aligned, the only values which make sense are 3 (quadword aligned) and 4 (octaword aligned).
For example, to force data to be quadword aligned:
code_1'(align +3); <<TABLE>> data_1'(quad_word, L0'ref); data_1'(quad_word, L1'ref);
LA (Load Address)
This instruction generates one or more machine instructions to load the effective address of an external or X'Ref operand into a register.
Code_2'(La, T0, Table'ref);
An implicit LA instruction may be generated by for X'Ref or external operands in other machine_code instructions.
A warning is generated for these implicit LA instructions if pragma Implicit_Code(off) is used.
Implicit Register Use
Register R28 (AT) is reserved by the compiler for use as a temporary register.
Most of the synthetic pseudo-instructions require additional temporary registers. The instructions and the registers they used are shown in the table below.
Programming Examples
Program Control
Labels and subroutine names are used in conjunction with the X'Ref attribute to form destinations for branch and subroutine linkage instructions.
An arbitrary call interface can be set up using JSR (or BSR). For example:
Code_2'(Mov, +1, A0); Code_2'(Jsr, Ra, Proc'Ref);
It is up to the programmer to establish the correct parameter interface expected by the called routine (in this case, passing a value in a0).
The compiler will automatically code to load the address of the called procedure into the procedure value register (PV). It will also generate code to restore the global pointer register (GP), after the call, if GP addressing is used in the routine.
The following example illustrates a hypothetical start-up routine for an Ada program. Its function is to call an initialization routine, and then invoke each entry in the elaboration table that is constructed by the pre-linker.
procedure V_Start_Program_Continue is use Machine_Code; begin -- Mark the frame pointer to indicate top of stack for the program. Code_2'(Mov, Zero, Fp); -- Initialize the Ada "new" memory allocation Code_2'(Lda, A0, Configuration_Table'Ref); Code_1'(Jsr, V_Usr_Conf_I.Aa_Init'Ref); -- Index through the elaboration table setup by a.ld and elaborate -- each package in the program. -- -- First, save away the address of <<Stop_Lab>>. -- It's used to "stop" exception unwinding. Code_2'(Lda, T0, Stop_Lab'Ref); Code_2'(Lda, T1, V_Usr_Conf_I.Stop'Ref); Code_2'(Stq, T0, Disp (T1, +0)); Code_2'(Lda, S0, Ext ("ELABORATION_TABLE")); <<Elab>> Code_2'(Ldq, Pv, Disp (S0, +0)); Code_2'(Lda, S0, Disp (S0, +8)); Code_2'(Beq, Pv, Elab_Done'Ref); Code_1'(Jsr, Pv); <<Stop_Lab>> Code_1'(Br, Elab'Ref); <<Elab_Done>> -- Shouldn't ever get here. -- Exit the user program Code_2'(Mov, Zero, A0); Code_1'(Jsr, V_Usr_Conf_I.Ada_Exit'Ref); -- No return back end V_Start_Program_Continue; procedure V_Start_Program is -- V_START_PROGRAM is the startup routine of the Ada program. pragma Implicit_Code (Off); pragma Optimize_Code (Off); use Machine_Code; begin -- Upon startup, GP and PV are bogus. Compute new GP based on -- current PC. Code_2'(Br, T0, L1'Ref); -- T0 := L1 - 4 Code_0'(Op => Nop); <<L1>> Code_3'(Addq, T0, +8, T0); -- T0 := Address of ldgp (ldah) instr. Code_2'(Ldgp, Gp, Disp (T0, 0)); Code_2'(Mov, Sp, T0); Code_2'(Ldl, A0, Disp (Sp, +0)); -- argc Code_2'(Lda, A1, Disp (Sp, +8)); -- argv Code_3'(S8Addq, A0, A1, A2); Code_3'(Addq, A2, +08, A2); -- envp -- Save my return address and make room on the stack for a build area. Code_3'(Subq, Sp, +64, Sp); Code_2'(Stq, Ra, Disp (Sp, +8)); -- Zero stack limit Code_2'(Mov, Zero, S5); -- Save a pointer to argv and envp Code_2'(Stq, T0, V_Usr_Conf_I.U_Mainp'Ref); Code_2'(Stq, A2, V_Usr_Conf_I.Environ'Ref); -- Set current data limit so that its less than HEAP_MAX. On -- some OSs the program hangs or is killed if sbrk's cause -- the data area to get too big. -- -- Also, change the current stack limit if its less than -- MAIN_STACK_SIZE + EXCEPTION_STACK_SIZE Code_1'(Jsr, Set_Resource_Limits'Ref); -- Initialize program specific user data Code_2'(Lda, A0, Configuration_Table'Ref); Code_1'(Jsr, V_Init_Usr_Data'Ref); -- Initialize attribute records Code_2'(Lda, A0, Configuration_Table'Ref); Code_1'(Jsr, V_Init_Attr'Ref); -- Boot/initialize tasking and heap/pool data structures Code_2'(Lda, A0, V_Usr_Conf_I.Usr_Link_Block'Ref); Code_2'(Lda, A1, Configuration_Table'Ref); Code_2'(Lda, A2, V_Usr_Data.Main_Pragmas'Ref); Code_2'(Lda, A3, V_Start_Program_Continue'Ref); Code_1'(Jsr, V_Usr_Conf_I.Ts_Initialize'Ref); -- If ts_initalize returns then, main task executes on another -- stack and the main program has been terminated. end V_Start_Program;Jump Table via Addresses
A jump table can be constructed by building a table of addresses. The table is built by using the data statement, where the operands consist of label references to the selected entry points. The following program fragment illustrates the technique.
with Machine_Code; procedure Example (Index : Integer) is -- Assume index has value 0, 1, ..., n use Machine_Code; begin Code_2'(La, T0, Table'Ref); -- Load address of TABLE Code_3'(S8Addq, Index'Ref, T0, T0); -- Calculate address of entry Code_2'(Ldq, T1, Disp (T0)); -- Load address from TABLE Code_2'(Jmp, R31, T1); -- Branch to destination Code_1'(Align, +3); -- Quadword align TABLE <<Table>> Data_1'(Quad_Word, L0'Ref); Data_1'(Quad_Word, L1'Ref); -- ... Data_1'(Quad_Word, Ln'Ref); <<L0>> Code_0'(Op => Nop); -- ... Code_2'(Br, R0, Done'Ref); -- Branch to epilogue code <<L1>> Code_0'(Op => Nop); -- ... Code_2'(Br, R0, Done'Ref); <<Ln>> Code_0'(Op => Nop); -- ... <<Done>> Code_0'(Op => Nop); -- Target statement for label -- Compiler-generated epilogue code here. end Example;Each code segment in the table must branch to the end of the routine so that the compiler-generated epilogue code for the routine is executed.
Replacing the branch with a return instruction would not be correct, unless the routine was written with Pragma Implicit_Code(Off). In which case, it would be entirely up to the programmer to handle the subroutine linkage.
Machine Code Insertions - Intel ArchitectureThis section covers the details of machine code insertions for the IA-32 architecture. Be sure to read the Machine Code Insertions Overview before proceeding with this section.
Opcodes
The type Opcode is an enumeration type declared in the package Machine_Code. The type defines all of the supported instructions for the IA-32 architecture.
Instructions are named by their standard mnemonics, except where those mnemonics would conflict with an Ada reserved word (e.g. AND, IN, LOOP, NOT, OR, OUT and XOR). In those cases the mnemonic is suffixed with "_Op".
Operands and Addressing Modes
The type Operand is a private type declared in the package Machine_Code. The package also provides constants of type Operand and functions that return values of type Operand. The Apex-defined attribute X\QRef can be applied to most Ada objects and denotes a value of type Operand.
All the supported IA-32 registers are provided as Operand constants, including the segment registers, control registers, debug registers, and test registers.
All the supported IA-32 addressing modes are made available by functions that denote values of the type Operand. For example, the following code-statement stores the value in EBX at the address pointed to by EAX
Code_2'(Mov, Base (Eax), Ebx);
The following table summarizes the available addressing modes and the functions used to implement them.
There are a set of type operators which specify the access size of a memory address. The following table summarizes the available operators and their access size.
Table 16 Available Functions and Access Size
Function Access Size
BYTE(mem) 8-bits
WORD(mem) 16-bits
DWORD(mem) 32-bits
PWORD(mem) 48-bits
QWORD(mem) 64-bits
TBYTE(mem) 80-bits
UNTYPED(mem) N/A
The type operator Untyped can only be used if the context already indicates the access size. This is useful for those instructions (e.g. FLDENV, FRSTOR, FSAVE and FNSAVE) whose access size does not fall into one of the sized categories.
The following forms show the syntax of forming an addressing mode:
addr_func(...) SEGMENT(sreg, addr_func(...)) size_func(addr_func(...)) size_func(SEGMENT(sreg, addr_func(...)))
Examples
Code_2'(Mov, Eax, Esp); Code_2'(Mov, Eax, Base (Esp)); Code_2'(Lea, Eax, Index (Eax, 4, 8)); Code_2'(Mov, Ebx, Base_Index (Ebx, Edx, 4, 0)); Code_1'(Inc, Byte (Segment (Cs, Base (Esp)))); Code_1'(Inc, Dword (Index (Eax, 4, 8))); Code_2'(Lea, Edx, Ext ("SYMBOL")); Code_1'(Fsave, Untyped (Base (Ebp)));
The operands are used to determine which instruction is to be selected. For example,
Code_2'(Mov, Eax, +1);
provides enough information to determine that the instruction is a move immediate dword to a register, because the register EAX indicates that the operation type is dword. The following example,
Code_2'(Mov, Base (Eax), +1);
is now ambiguous, because an anonymous access type is specified and the immediate value can be of any size. The address mode must specify the access size. The following example chooses the instruction move immediate byte indirect through a register.
Code_2'(Mov, Byte (Base (Eax)), +1);
Code_2'(Mov, Base (Eax), Ax)
does not require that an access type be specified, since the register AX, determines that it is a word (i.e. 16-bit) operation.
The Base, Index and Base_Index functions are overloaded to allow the displacement to be specified as a numeric (integer) or as a label reference (operand). Specifying a negative numeric reference, such as -4, can be interpreted as an integer or an operand, due to the overloading of "-". Use qualification or named notation within the function to resolve the overloading of Base, Index and Base_Index. For example,
Code_2'(Mov, Eax, Base (Eax, Integer'(-4))); Code_2'(Mov, Eax, Base (Eax, Disp_Int => -4));
All arguments to machine code functions must be one of the following:
- static expression (cf. Ada RM 4.9)
- type conversion (the expression operand must be a static expression)
- string literal
- representation attributes (cf. Ada RM 13.7.2)
- X\QRef attribute
- entity defined in the package Machine_Code
Ada Entities as Operands
Objects visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute. In many cases, using X'Ref will cause more than a single instruction to be generated.
Labels
When the X\QRef attribute is applied to labels, the address generated is an offset or a branch displacement depending on the instruction. For example, the following instructions generate an offset for Label'Ref. Note that the first instruction is used to obtain the address of Label. The second instruction moves the double word at location Label.
<<Label>> ... Code_2'(Lea, Eax, Label'Ref); -- Address of Label Code_2'(Mov, Eax, Label'Ref); -- Value at Label
The following instruction uses a branch displacement.
Code_1'(Jmp, Label'Ref); -- Jump to LABEL
Subprograms
When the X\QRef attribute is applied to subprograms, the address generated is an offset.
Prefix Overrides
A prefix override can be inserted into the code stream by using a data statement with the appropriate value, preceding the code statement. For example,
Data_1'(Byte, +16#67#); -- Address-size override Data_1'(Byte, +16#66#); -- Operand-size override Code_2'(Mov, Eax, Base (Ebx));
Floating Point
Floating-point operations are invoked directly by machine instructions which access the IA-32 FPU. The package Machine_Code provides the opcodes and floating-point registers so that the FPU can be referenced directly from machine-code insertions.
For example, the FPU provides instructions that perform trigonometric functions. The Ada function below returns the partial tangent of a value given in degrees. It calls a machine code procedure that uses the FPU to first convert the degrees to radians and then to take the partial tangent using the FPTAN instruction.
with Machine_Code; function Ptan (X : Float) return Float is Degrees_180 : Float := 180.0; Result : Float; procedure Fptan (Theta : Float; Result : out Float) is use Machine_Code; begin Code_1'(Fld, Degrees_180'Ref); Code_0'(Op => Fldpi); Code_0'(Op => Fdivr); -- Degrees to radians ratio (180/pi) Code_1'(Fld, Theta'Ref); Code_0'(Op => Fdiv); -- THETA in radians {THETA/(180/pi)} Code_0'(Op => Fptan); -- tan(THETA) = X/Y Code_0'(Op => Fdivr); Code_1'(Fst, Result'Ref); end Fptan; pragma Inline_Only (Fptan); begin Fptan (X, Result); return Result; end Ptan;Note: In the Apex Ada types Float and Short_Float correspond to a Short Real on the IA-32 FPU, while the type Long_Float corresponds to a Long Real. No Apex Ada type corresponds to Temporary Real.
Programming Examples
Start-Up Routine
Operands are used to control the flow of execution with instructions such as Call, Jcc and Loop/Loopcond. Labels and subroutine names can be used in conjunction with the X\QRef attribute to form destinations for these instructions.
The following example illustrates a hypothetical start-up routine for an Ada program. Its function is to call an initialization routine, and then invoke each entry in the elaboration table that is constructed by the pre-linker.
with Machine_Code; procedure Startup is use Machine_Code; pragma Implicit_Code (Off); begin -- User/Board/Default initializations. Code_1'(Call, Dword (Ext ("CONFIG_INIT"))); -- Store the return pc. Used by exception handling routines -- to prevent unwinding past this frame. Code_2'(Lea, Eax, Stop_Lab'Ref); Code_2'(Mov, Ext ("STOP"), Eax); -- The elaboration table is built by a.ld, it is built -- as an array containing the addresses of the library -- units to be elaborated, followed by the address of -- the main program, and ending with a null address. Code_2'(Xor_Op, Edx, Edx); -- EDX := 0 <<Elab>> Code_2'(Lea, Ebx, Ext ("ELABORATION_TABLE")); Code_2'(Mov, Ebx, Base_Index (Ebx, Edx, 4, 0)); Code_2'(Test, Ebx, Ebx); Code_1'(Jz, Done'Ref); Code_1'(Push, Edx); -- save register Code_1'(Call, Ebx); <<Stop_Lab>> Code_1'(Pop, Edx); -- restore register Code_1'(Inc, Edx); Code_1'(Jmp, Elab'Ref); <<Done>> Code_1'(Push, Eax); Code_1'(Call, Dword (Ext ("HALT"))); end Startup;
Machine Code Insertions - M68k FamilyThis section covers the details of machine code insertions for the Motorola M68K Architecture Family. Be sure to read the Machine Code Insertions Overview before proceeding with this section.
Opcodes
The type Opcode is an enumeration type declared in the package Machine_Code. The type provides all of the supported instructions for the M68000 Family, as well as the instructions for the MC68851 and MC68881/MC68882. Instructions are named by their standard mnemonics with a suffix of _size where applicable. For example, the three TST instructions are represented by the enumeration values TST_B, TST_W and TST_L. The branch instructions Bcc, BRA, cpBcc, cpDBcc and DBcc do not use a _size suffix. The operand determines the size of the instruction.
Operands and Addressing Modes
The type Operand is a private type declared in the package Machine_Code. The package also provides constants of type Operand and functions that return values of type Operand. The Apex-defined attribute X'Ref can be applied to most Ada objects and denotes a value of type Operand.
All the M68000 family, MC68851 and MC68881/MC68882 registers are provided as Operand constants (including registers such as the CCR, SR and the CACR). For example, the following code-statement moves the contents of the CCR to D0:
Code_2'(MOVE_B, CCR, D0);
All the M68000 addressing modes are made available by functions that denote values of type Operand. For example, the following code-statement stores the contents of the location pointed to by A1 at the absolute address 16#BF#.
Code_2'(MOVE_L, INDR(A1), ABSOL(16#BF#, L));
Using an addressing mode that is not allowed on your processor will cause an illegal instruction exception to be raised at run-time.
The following tables summarize the available addressing modes and the functions used to implement them. The first table covers the addressing modes available on all M68000 family processors. The second table lists addressing modes available only on the MC68020/30/40/60 processors.
Table 17
Addressing Modes for M68000 Family Processors
Examples
Code_2'(ADDQ_B, +2, D1); Code_2'(MOVE_B, IMMED(`#'), INDR(A2)); Code_2'(MOVE_L, INCR(A1), INCR(A2)); Code_2'(MOVE_L, +0, MEMORY(DISP(A0, 4), D0, W, 2, 16)); Code_2'(MOVE_L, +0, MEMORY(INDEX(A0, 2, D1, L, 4), 8)); Code_2'(MOVEM_L, D0/D1/A1, DISP(A3, 4));
All arguments to machine code functions must be one of the following:
- static expression (cf. RM 4.9)
- type conversion (the expression operand must be a static expression)
- string literal
- representation attribute (cf. RM 13.7.2)
- X'Ref attribute
- entity defined in the package Machine_Code
The Disp and Index functions are overloaded to allow the displacement to be specified as a numeric (integer) or as a label reference (operand). The latter is useful in generating PC-relative addresses. Specifying a negative numeric reference, such as -4, can be interpreted as an integer or an operand, due to the overloading of "-". Use qualification or named notation within the function to resolve the overloading of Disp or Index. For example:
Code_2'(MOVE_L, +1, DISP(A0, INTEGER'(-4))); Code_2'(MOVE_L, +1, DISP(A0, INT => -4));
For the index and memory-indirect addressing modes of the MC68020, the address-register and/or index-register can be suppressed. The suppressed register mnemonics ZA0-ZA7, ZD0-ZD7 and ZPC are supplied for this purpose. For example, if a data register (Dn) is used as the index register and the address register is suppressed, then a <#007F>data register indirect" access can be generated.
Code_2'(MOVE_L, +0, INDEX(ZA0, INTEGER'(+4), D0, L));
Instructions that use a bit field selector ({offset:width}) or a pair designator (Rn:Rn) use no special syntax in a machine code statement, each component is specified as a single operand. For example, the instruction BFEXTU has the following syntax:
BFEXTU <ea>{offset:width}, Dn
code_4'(BFEXTU, <ea>, offset, width, Dn);
Ada Entities as Operands
Objects visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute. In many cases, using X'Ref will cause more than a single instruction to be generated.
Labels
When the X'Ref attribute is applied to labels, the addressing mode generated is PC-relative or a branch displacement depending on the instruction. For example, the following instructions generate a PC-relative addressing mode for Label'Ref. Note that the first instruction is used to obtain the address of Label. The second instruction moves the long word at location Label. The third instruction jumps to location Label.
Code_2'(LEA, LABEL'REF, A0); -- address of LABEL Code_2'(MOVE_L, LABEL'REF, D0); -- value at LABEL Code_1'(JMP, LABEL'REF); -- jump to LABEL
The following instruction uses a branch displacement. Note that the size of the instruction is determined by the size of the displacement.
Code_1'(BRA, LABEL'REF); -- branch to LABEL
Floating-Point
The Apex compiler generates hardware floating point instructions when compiling against the MC68881, MC68040 or MC68060 standard libraries, and package machine_code includes the opcodes and registers for the M68k floating-point instruction set. For example, the MC68881 provides instructions that perform trigonometric functions. The Ada function below returns the sine of a value given in degrees. It calls a machine code procedure that uses the MC68881 to first convert the degrees to radians and then to determine the sine using the FSIN instruction.
function Sin (X : Degrees) return Long_Float is Result : Long_Float; procedure Fsin (X : Degrees; Result : out Long_Float) is use Machine_Code; begin Code_2'(Fmove_W, +180, Fp1); Code_2'(Fmovecr_X, +Pi, Fp2); -- constant pi is an offset in ROM Code_2'(Fdiv_D, Fp2, Fp1); -- degrees to radians ratio (180/pi) Code_2'(Fmove_D, X'Ref, Fp0); Code_2'(Fdiv_D, Fp1, Fp0); -- X in radians {X/(180/pi)} Code_2'(Fsin_D, Fp0, Fp0); -- sine of X Code_2'(Fmove_D, Fp0, Result'Ref); end Fsin; begin Fsin (X, Result); return Result; end Sin;Note: In Apex Ada the type Long_Float corresponds to double precision on the MC68881/MC6882, while the types Short_Float and Float correspond to single precision.
The MC68040 and MC68060 do not support the entire MC68881/2 instruction set. Instructions that are not directly supported are emulated in software through traps to the kernel.
Programming Examples
Device I/O
One of the primary reasons to use machine code insertions is to write an interface to a hardware device.
For example, suppose a board provides an I/O routine that has the following interface specification.
- The routine is entered by performing a Trap 15 instruction.
- A control code is passed in D0 (16#000A# = character output).
- The output character is passed D1.
The following code shows how machine code insertions could be used to output a string of characters to this I/O routine. Note that the code in this example is not written to be efficient, but to demonstrate different aspects of machine_code insertions.
with System; use System; with Machine_Code; procedure String_Io (Chars : String) is type Bit is range 0 .. 1; type Register is array (1 .. 32) of Bit; for Register'Size use 32; Regs : array (1 .. 2) of Register; -- Save registers here during call Arg1 : Character; -- Named reference for Call_1 parameter Io_Trap : constant := 15; Char_Out : constant Short_Integer := 16#000A#; procedure Write_Char (Char : Character) is use Machine_Code; begin Code_2'(Move_W, Io_Code'Ref, D0); -- Move I/O code to D0 Code_2'(Move_B, Char'Ref, D1); -- Move char to D1 Code_1'(Trap, +Io_Trap); -- call I/O routine Code_1'(Bra, Skip_Data'Ref); -- Don't execute data! <<Io_Code>> Data_1'(Word, Char_Out'Ref); Data_1'(Word, +16#00FF#); -- Padding <<Skip_Data>> Code_0'(Op => Nop); -- Need target for label -- Must execute epilogue end Write_Char; procedure Send_Chars (Chars : Address; Length : Short_Integer) is use Machine_Code; begin Code_2'(Movea_L, Chars'Ref, A0); -- Load pointer to string into A0 Code_2'(Move_W, Length'Ref, D1); -- Load length into D1 Code_2'(Subq_W, +1, D1); -- Dbf falls through when D1 < 0 <<Top_Of_Loop>> Code_2'(Move_B, Incr (A0), Arg1'Ref); -- Move Char to Arg1 Code_2'(Movem_L, D1 / A0, Regs'Ref); -- Save registers Call_1'(Write_Char'Ref, Arg1'Ref); -- Call Write_Char Code_2'(Movem_L, Regs'Ref, D1 / A0); -- Restore registers Code_2'(Dbf, D1, Top_Of_Loop'Ref); -- Branch to top if not done end Send_Chars; begin if Chars'Length > 0 then Send_Chars (Chars (1)'Address, Chars'Length); end if; -- The invocation of Send_Chars() is equivalent to the following: -- for I in Chars'Range loop -- Write_Char(Chars(I)); -- end loop; end String_Io;Bit Operations
Another valid reason to use machine code insertions is to directly access hardware instructions in order to generate efficient code.
The follow example shows how to write efficient interface to use hardware instructions to perform common bit operations.
package Bit_Ops is function Bit_And (L, R : Integer) return Integer; pragma Inline_Only (Bit_And); end Bit_Ops; with Machine_Code; package body Bit_Ops is procedure B_And (L, R : Integer; X : out Integer) is use Machine_Code; begin Code_2'(Move_L, L'Ref, D0); Code_2'(Move_L, R'Ref, D1); Code_2'(And_L, D0, D1); Code_2'(Move_L, D1, X'Ref); end B_And; pragma Inline_Only (B_And); function Bit_And (L, R : Integer) return Integer is X : Integer; begin B_And (L, R, X); return X; end Bit_And; end Bit_Ops;
Machine Code Insertions - RH-32This section covers the details of machine code insertions for the RH32 processor. Be sure to read the Machine Code Insertions Overview before proceeding with this section
Opcodes
The type Opcode is an enumeration type declared in the package Machine_Code. The type provides all of the instructions for the RH32 processor, including additional pseudo-instructions described below.
Instructions are named by their standard mnemonics, except where these would conflict with an Ada reserved word (e.g. Abs). In those cases the mnemonic is suffixed with "_Op".
Pseudo-Instructions
The Apex compiler accepts a series of "pseudo-instructions", which are either "macro instructions" that expand into one or more machine instructions, or instructions that control various details of code generation.
A subset of the RH32 defined instructions are really macro instructions; they are realized by one or more machine instructions. The Apex compiler will accept all of the RH32-defined macro instructions, though a small subset of them are currently unsupported and will generate a trap instruction. Also, a few additional macro instructions have been added to make machine code programming more convenient.
Following is a list of these macro instructions:
abs_op div fbgt fbo fint ld_b nor st_b
abs2 divo fble fbuo frem ld_h putio st_h
call fbe fblt fcvt_1750 fsqrt mul rem_op
calli fbge fbne fcvt_754 getio mulo return
Operands and Addressing Modes
Type Operand is a private type declared in the package Machine_Code. The package also provides constants of the type Operand and functions that return values of type Operand. The Apex defined attribute X'Ref can be applied to most Ada objects and denotes a value of type Operand.
All the supported RH32 registers are provided as Operand constants.
All the supported RH32 addressing modes are made available by functions that denote values of type Operand. The following table summarizes the available addressing modes and the functions used to support them.
Examples
Code_1'(Call, Subp'Ref); Code_2'(Ld, R2, Disp (R1, +4)); Code_2'(Lda, S5, Label'Ref); <<Label>> Code_2'(Ld, T0, Disp (R1, Const_Offset)); Code_2'(Lda, T3, Ext ("_END")); Code_2'(Ld, V0, Disp (R1, -16#40000#)); Code_2'(Lda, T2, Subp'Ref); Code_2'(Ld, T2, Y'Ref); Code_2'(Ldi, T3, +Const_Offset); Code_2'(Ldi, T3, Immed (Const_Offset)); Code_2'(Ldi, T3, Immed (Const_Offset - 4)); Code_2'(Ldi, T3, Immed_Mod32 (16#FFFF_FFFF#));
All arguments to machine code functions must be one of the following:
- static expression (cf. RM 4.9)
- type conversion (the expression operand must be a static expression)
- string literal
- representation attributes (cf. RM 13.7.2)
- X'Ref attribute
- entity defined in the package Machine_Code
Ada Entities as Operands
Objects visible to a machine code procedure (either in packages or enclosing subprograms) can be referenced using the X'Ref attribute. In many cases, using X'Ref will cause more than a single instruction to be generated to form the address of the referenced object.
Labels
When the X'Ref attribute is applied to labels, the addressing mode generated is a branch displacement or evaluates to an address depending on the instruction.
Code_1'(Bra, LabeL'REF); -- Branch to Label (branch displacement) Code_1'(Jmp, Label'Ref); -- Address of Label (absolute)
Subprograms
When the X'Ref attribute is applied to subprograms, the reference denotes the address of the subprogram.
Extended Addressing
RH32 instructions that reference memory, or generate memory addresses, use a (base + displacement) calculation to generate the memory address. The basic memory reference instruction is encoded in 32 bits with 16 bits used to hold the displacement value. In situations where the desired displacement exceeds the range -32,768..32,767, we call the address an extended address. When the Ada compiler detects an extended address, it generates additional machine instructions to properly calculate the (base + displacement). Extended addressing allows a displacement exceeding the range of -32,768 to 32,767 for the following instructions:
fld fst ld_d ld_b st st_ getio linkm
fld_d fst_d ld lda ld_h st_d st_h putio
In most cases involving X'REF, implicit code is generated to form the address.
Floating Point
Floating-point operations are invoked directly by machine instructions which access the RH32 floating point coprocessor. The Machine_Code package provides the opcodes and floating-point registers so that the coprocessor can be referenced directly from machine code insertions.
The operands consist of the general purpose registers, special purpose registers (e.g. Hop, Lop, Cpu_Detail), floating-point registers F0-F15, based addressing, literals and X'Ref (where applicable).
Code_3'(FSUB, F0, F1, F2);
Assembly Language Correspondence
The RH32 instruction set has a rich set of instructions of varying length. Often, there are several instructions that can be used to achieve the same purpose. The RH32 assembly language is defined in a way that most of the choices for the best machine instruction sequence is made by the assembler program, unless the programmer provides an explicit override. For example, if the following assembly language instruction was coded:
ADD T0,#5
This would generate a 16-bit instruction that uses the immediate form to access the immediate value of 5. However, if the programmer wanted the 32-bit form of the add immediate instruction, they would write:
ADD* T0,#5
Where the "*" above indicates an override to the assembler, telling the assembler to use the longer form of the Add instruction, even if a shorter instruction might calculate the same value. Often, this override is used to explicitly select a 32-bit instruction when filling a branch delay slot.
As noted above, Apex always fills delay slots, and has no method to let you explicitly fill a delay slot. However, there may be other times when you want to closely control the instructions generated by a given machine code instruction. For this reason, the Apex machine code package has been defined in such a way that most of possible assembly language constructs can be written as an Ada machine code insertion.
The Apex machine code definition places some restrictions on the method that a particular assembly language instruction can be expressed in Ada. Namely:
- Each value in machine_code's enumeration, `opcode', can be used only with a fixed number of operands. For example, if `add' is defined to take three register operands as in "Code_3'(Add, R3, R2, R1)", it cannot also appear with only two operands, as in "Code_2'(Add, R2, R1)".
- A particular value in the `opcode' enumeration must correspond to a single machine instruction (with macro instructions as the obvious exception). Thus, if "ldi" is defined to accept a 16-bit signed immediate value, the generated RH32 machine instruction would be 32-bits long. For example, even if the immediate value used with "ldi" fits in a signed 5-bit field, the Apex compiler will not generate the shorter 16-bit form of the load immediate Rh32 machine instruction.
The RH32 machine code package deals with the difficulties by inventing unique opcode names, when the same machine operation may have a differing number of operands, or where the size of the operands (as in the case of immediate values) might generate different machine instructions.
Programming Examples
Block Compare
The following example compares two blocks of memory and returns 0 if the blocks are equal, or the signed difference of the first bytes that differ.
procedure Cmpc3 (Addr1 : Address; Addr2 : Address; Lgth : Storage_Count) is use Machine_Code; begin Code_3'(Bleu, Lgth'Ref, +0, Equal'Ref); Code_3'(Addu, T2, Addr1'Ref, Lgth'Ref); <<Cmp>> -- Perform the test for equality as an unsigned compare Code_2'(Ld_B, T0, Disp (Addr1'Ref, +0)); Code_2'(Ld_B, T1, Disp (Addr2'Ref, +0)); Code_3'(Bne, T0, T1, Not_Equal'Ref); Code_3'(Addiu, Addr1'Ref, Addr1'Ref, +1); Code_3'(Addiu, Addr2'Ref, Addr2'Ref, +1); Code_3'(Bne, Addr1'Ref, T2, Cmp'Ref); <<Equal>> Code_2'(Ldq, V0, +0); Code_1'(Bra, Done'Ref); <<Not_Equal>> -- Bytes aren't equal, extend signs and compute the difference Code_2'(Shli, T0, +24); Code_2'(Shrai, T0, +24); Code_2'(Shli, T1, +24); Code_2'(Shrai, T1, +24); Code_3'(Subu, V0, T0, T1); <<Done>> Code_0'(Op => Nop); end Cmpc3;Jump Table via Addresses
A jump table can be constructed by building a table of addresses. The table is built by using the data statement, where the operands consist of label references to the selected entry points. The following program fragment illustrates the technique.
with Machine_Code; procedure Jump_Table (Index : Integer) is use Machine_Code; -- Assume INDEX has the values 0, 1, ..., n begin Code_2'(Lda, T0, Table'Ref); -- load address of TABLE Code_2'(Move, T1, Index'Ref); -- scale INDEX Code_2'(Shli, T1, +2); -- Code_2'(Addu2, T0, T1); -- calculate TABLE address Code_2'(Ld, T1, Base (T0)); -- load address from TABLE Code_1'(Jmpi, T1); -- branch to destination <<Table>> Data_1'(Word, L0'Ref); Data_1'(Word, L1'Ref); -- ... Data_1'(Word, Ln'Ref); <<L0>> -- ... Code_1'(Bra, Done'Ref); <<L1>> -- ... Code_1'(Bra, Done'Ref); <<Ln>> -- ... Code_1'(Bra, Done'Ref); <<Done>> Code_0'(Op => Nop); end Jump_Table;Each code segment in the table must branch to the end of the routine so that the compiler-generated epilogue code for the routine is executed.
Replacing the branch with a return instruction would not be correct, unless the routine was written with Pragma Implicit_Code(Off). In which case it would be entirely up to the programmer to handle the subroutine linkage.
Rational Software Corporation http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2004, Rational Software Corporation. All rights reserved. |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |