Assembly Statements This utility is available only with Rational Apex for Rational Exec and Rational Apex Embedded for Tornado.
Rational Software Corporation provides the asm.h include file for Rational Exec as a machine language interface to the target processor. For Tornado you must use the VxWorks supplied asm.h file
Note: Assembly statements are supported only in the Apex Duo product for the PowerPC.
ASM statements provide, from within the C and C++ languages, low-level access to the processor that is normally available only from assembly language.
Use of ASM statements 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.
ASM statements can be intermixed with normal C/C++ statements and provide features such as the full range of addressing modes and parameters that enable the programmer to integrate the machine code into surrounding code with minimal effort.
ASM statements 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 should be used with discretion and only where absolutely necessary.
PowerPC Assembly Statements
- ASM Statements
- Program Control
- Subprogram Call
- Parameter Passing in Assembly Subprograms
- Local Data
- Floating Point
- Pragmas
- Debugging Assembly Statements
- File asm.h
ASM Statements
An ASM statement is used to specify the machine instruction and any operands needed by the instruction. The form of this construct is:
asm(opcode, operands);
Opcodes
Type Opcode is an enumeration type declared in asm.h. The type provides all of the instructions for the PowerPC. Instructions are named by their standard mnemonics, except for the instructions abs, and, or and xor which are appended by _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
Type Operand is an intrinsic type declared in asm.h. This file also provides constants of the type Operand and functions that return values of type Operand.
All the PowerPC registers are provided as enumeration constants.
All the PowerPC 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.
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
- entity defined in file asm.h
C/C++ Entities as Operands
It is sometimes necessary to reference C/C++ constants and variables from within an ASM function. It is very tedious and error-prone for a programmer to attempt to calculate these references by hand. Apex provides the ADDR() function, which generates a reference to the entity X. The definition is similar to the & operator.
For a prefix X that denotes an object, a program unit, or a label ADDR(X) yields the reference of the first of the storage units allocated to X. For a label or subprogram, the value refers to the machine code associated with the corresponding statement.
In some cases, using ADDR() causes more than a single instruction to be generated.
char X[3];
asm(LBZ, R0, ADDR(X[2]));
liu r3, 0 @X lbz r0, 01(r3) @X
When X is constant static, the value used is an immediate literal.
When the ADDR() function 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.
asm(BEQ,CR0, ADDR(LABEL)); -- branch to LABEL
The following instruction evaluates the address.
asm(LA, R3, ADDR(LABEL)); -- address of LABEL
When the address function is applied to subprograms, the reference denotes the subprogram. The following instruction yields the address that references the machine code of the subprogram.
asm(LA, R3,ADDR(SUBP));
Extended addressing allows a displacement exceeding the range of -32,768 to 32,767 for the following instructions:
Table 3 Extended Addressing
LA lfdu lhz stb stfsu
LWZ lfs lhzu stbu sth
lbz lfsu lmw stfd sthu
lbzu lha lwzu stfdu stmw
lfd lhau stw stfs stwu
In most cases involving ADDR(), implicit code is generated. In particular, for any label, external reference or extended addressing.
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 ADDR() function to form destinations for these instructions.
The following example illustrates an ASM subprogram with typical control flow characteristics. This routine reads a table describing what needs to be copied and then performs the copy.
void init_ram() { /* ** Perform ROM to RAM copy of the program's initialized data. ** ** Copy rom data to ram images. The values of statically ** initialized data for ROM'd programs must reside in ROM, but ** the variables must be in RAM to be modifiable. Thus, both ROM ** and RAM locations are specified in the linker directives file ** for each data section. The following code copies the data ** values from the ROM locations to the corresponding RAM ** locations. */ asm( LA, R10, DATA_INIT_TABLE); do_copy: asm( LWZ, R11, ADDR(R10)); /* byte count */ asm( CMPI, CR0, R11, +0 ); /* if 0, all done */ asm( BEQ, CR0, done_copy); /* if 0, all done */ asm( LWZ, R12, ADDR(R10+4)); /* ram */ asm( LWZ, R15, ADDR(R10+8)); /* rom */ move_bytes: asm( LBZ, R16, ADDR( R15 )); /* get byte */ asm( STB, R16, ADDR( R12 )); /* store byte */ asm( ADDI, R11, R11, -1 ); /* decrement byte count */ asm( ADDI, R12, R12, +1 ); /* increment ram address */ asm( ADDI, R15, R15, +1 ); /* increment rom address */ asm( CMPI, CR0, R11, +0 ); /* if 0, all done */ asm( BGT, CR0, move_bytes); asm( ADDI, R10, R10, +12 ); /* next entry */ asm( B, do_copy); /* repeat */ done_copy: return; }Subprogram Call
Since you can intermix ASM statements with normal C/C++ statements, it is best to code a call in standard C/C++. When you have to resort to using ASM statements to make the call, you must understand the calling conventions of your compiler.
Parameter Passing in Assembly Subprograms
On RISC machines, Apex passes one or more parameters in registers. It is important to understand exactly how registers are used in parameter passing, especially if you are implementing subprograms that use the ADDR() function on parameters. Attempting to use the ADDR() function on a parameter held in a register in an instruction where a memory reference is required results in the compiler flagging an error. Likewise, using the ADDR() function on a parameter held in a memory location in an instruction requiring a register also results in an error.
ASM expects references to parameters must be consistent with the register usage rules for your system. For example, on PowerPC-based systems, the compiler passes the first 8 scalar parameters in registers r3-r10. The LWZ instruction is used to move a value from a memory location into a register while the mr instruction is used to transfer a value from one register to another. Given the following example:
int test_machine_code (p1,p2,p3,p4,p5,p6,p7,p8,p9 : integer) { asm(MR, R10, p1);); // (A) put p1 into register r10 asm(LWZ, R11, p9); // (B) put p9 into register r11 ...Since the first 8 scalar parameters are passed in registers, p1 is in a register, while p9 is on the stack. The compiler knows which parameters are on the stack and which are passed in registers.
Local Data
Variables that are visible to a machine code procedure directly or via the ADDR() function can be referenced. In some cases however, it may be necessary to intermix data and generated code. The ASM DATA statement is used to place a single data item in the code.
An Operand is restricted to the following:
- immediate
- label difference
asm(DATAB, '\n');; asm(DATAH, DIFF(L1, L2); asm(DATAW,0);Jump Table via Branch Offsets
A jump table can be constructed by using the data statement and label difference operator. The following program fragment, illustrates the technique.
#include <asm.h> int jump_table(register int index) { register unsigned jaddr; register int offset; asm(LA, jaddr, TABLE); asm(SLWI, offset, index, 2); asm(LWZX, offset, jaddr, offset); asm(ADD, jaddr, jaddr, offset); asm(MTCTR, jaddr); asm(BCTR); TABLE: asm(DATAW, DIFF(L1,TABLE)); asm(DATAW, DIFF(L2,TABLE)); asm(DATAW, DIFF(L3,TABLE)); L1: return 1; L2: return 2; L3: return 3; }
Floating Point
Floating point operations are invoked directly by machine instructions which access the FPU. ASM statements provide the opcodes and floating point registers so that the FPU can be referenced directly from ASM statements.
The operands consist of the floating point registers fr0-fr31, literals and float and double vars.
asm(FADD, FR0, FR1, FR2); register double pi = 3.14; register double third = 1.0/3.0;
The compiler assigns real FP registers for pi and third and initialize them as expected.
Pragmas
Two pragmas directly affect the generation of code for ASM statements: Implicit_Code and Optimize_Code.
#pragma implicit_code
Pragma implicit_code 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.) Implicit code also includes any additional code generated due to the use of the ADDR() function (such as code to load a base register).
When #pragma implicit_code off is specified, any stack allocation is not generated.
Implicit code is always generated for an ADDR() function which requires it. A warning message is generated when implicit_code off is specified in such a case.
#pragma optimize_code
Pragma optimize_code allows the programmer to specify whether the compiler should attempt to optimize through the machine code insertions. When #pragma optimize_code off is specified, the compiler generates the code as specified.
Debugging Assembly Statements
The Apex debugger supports source-level debugging of ASM statements. Breakpoints can be set at assembly-statements just like any other statements.
Register values can be determined using the reg command or by preceding the register name with a dollar sign and using either the p command or a word dump raw memory command. For example, the register r1 can be examined as a word decimal value using the line-mode command shown below.
$r1:Ld
The li and wi instructions can be used to disassemble the generated code. In addition, the debugger attempts to disassemble Data_x statements as PowerPC instructions, producing meaningless results.
File asm.h
The following is a listing of the Rational Exec asm.h for the PowerPC.
/* ** ASM Description for the PowerPC ** ** Copyright 1995 by Rational Software Corporation. All Rights Reserved. */ #ifndef _ASM_H_ #define _ASM_H_ enum OPCODE { ADD = 8, ADD_O, ADD_O_R, ADD_R, ADDC, ADDC_O, ADDC_O_R, ADDC_R, ADDE, ADDE_O, ADDE_O_R, ADDE_R, ADDI, ADDIC, ADDIC_R, ADDIS, ADDME, ADDME_O, ADDME_O_R, ADDME_R, ADDZE, ADDZE_O, ADDZE_O_R, ADDZE_R, AND_OP = 42, AND_R, ANDC, ANDC_R, ANDI_R, ANDIS_R = 48, B = 54, BA, BBF, BBFA, BBFC, BBFCL, BBFL, BBFLA, BBFR, BBFRL, BBT, BBTA, BBTC, BBTCL, BBTL, BBTLA, BBTR, BBTRL, BC, BCA, BCCTR = 76, BCCTRL, BCL, BCLA, BCLR, BCLRL, BCTR = 84, BCTRL, BDN, BDNA, BDNEQ, BDNGE, BDNGT, BDNL, BDNLA, BDNLE, BDNLR, BDNLRL, BDNLT, BDNNE, BDNNS, BDNSO = 101, BDZ, BDZA, BDZEQ, BDZGE, BDZGT, BDZL, BDZLA, BDZLE, BDZLR, BDZLRL, BDZLT, BDZNE, BDZNS, BDZSO = 117, BEQ, BEQA, BEQCTR = 122, BEQCTRL, BEQL, BEQLA, BEQLR, BEQLRL, BGE = 130, BGEA, BGECTR = 134, BGECTRL, BGEL, BGELA, BGELR, BGELRL, BGT = 142, BGTA, BGTCTR = 146, BGTCTRL, BGTL, BGTLA, BGTLR, BGTLRL, BL = 154, BLA, BLE, BLEA, BLECTR = 160, BLECTRL, BLEL, BLELA, BLELR, BLELRL, BLR = 168, BLRL, BLT, BLTA, BLTCTR = 174, BLTCTRL, BLTL, BLTLA, BLTLR, BLTLRL, BNE = 182, BNEA, BNECTR = 186, BNECTRL, BNEL, BNELA, BNELR, BNELRL, BNS = 194, BNSA, BNSCTR = 198, BNSCTRL, BNSL, BNSLA, BNSLR, BNSLRL, BSO = 208, BSOA, BSOCTR = 212, BSOCTRL, BSOL, BSOLA, BSOLR, BSOLRL, CMP = 229, CMPI, CMPL, CMPLI, CNTLZW = 235, CNTLZW_R, CRAND, CRANDC, CREQV, CRNAND, CRNOR, CROR, CRORC, CRXOR, DCBF, DCBI, DCBST, DCBT, DCBTST, DCBZ, DIVW = 262, DIVW_O, DIVW_O_R, DIVW_R, DIVWU, DIVWU_O, DIVWU_O_R, DIVWU_R, ECIWX = 275, ECOWX, EIEIO, EQV, EQV_R, EXTSB = 282, EXTSB_R, EXTSH, EXTSH_R, FABS = 288, FABS_R, FADD, FADD_R, FADDS, FADDS_R, FCMPO, FCMPU, FCTIW, FCTIW_R, FCTIWZ, FCTIWZ_R, FDIV = 302, FDIV_R, FDIVS, FDIVS_R, FMADD = 310, FMADD_R, FMADDS, FMADDS_R, FMR, FMR_R, FMSUB = 318, FMSUB_R, FMSUBS, FMSUBS_R, FMUL, FMUL_R, FMULS, FMULS_R, FNABS, FNABS_R, FNEG, FNEG_R, FNMADD = 332, FNMADD_R, FNMADDS, FNMADDS_R, FNMSUB = 338, FNMSUB_R, FNMSUBS, FNMSUBS_R, FRES, FRES_R, FRSP, FRSP_R, FRSQRTE, FRSQRTE_R, FSEL = 350, FSEL_R, FSUB, FSUB_R, FSUBS, FSUBS_R, ICBI, ISYNC = 358, LA = 360, LBZ = 362, LBZU, LBZUX, LBZX, LFD, LFDU, LFDUX, LFDX, LFS, LFSU, LFSUX, LFSX, LHA, LHAU, LHAUX, LHAX, LHBRX, LHZ, LHZU, LHZUX, LHZX, LIL, LIU, LMW = 386, LSWI = 390, LSWX, LWARX = 395, LWBRX, LWZ, LWZU, LWZUX, LWZX, MCRF = 406, MCRFS, MCRXR, MFCR, MFCTR, MFDAR, MFDBATL, MFDBATU, MFDEC, MFDSISR, MFEAR, MFFS, MFFS_R, MFIBATL, MFIBATU, MFLR, MFMSR, MFPVR, MFSDR1, MFSPR, MFSPRG, MFSR, MFSRIN = 429, MFSRR0, MFSRR1, MFTBL, MFTBU, MFXER, MR, MR_R, MTCR, MTCRF, MTCTR, MTDAR, MTDBATL, MTDBATU, MTDEC, MTDSISR, MTEAR, MTFS, MTFS_R, MTFSB0, MTFSB0_R, MTFSB1, MTFSB1_R, MTFSF, MTFSF_R, MTFSFI, MTFSFI_R, MTIBATL, MTIBATU, MTLR, MTMSR, MTSDR1, MTSPR, MTSPRG, MTSR, MTSRIN = 465, MTSRR0, MTSRR1, MTTBL, MTTBU, MTXER, MULHW = 475, MULHW_R, MULHWU, MULHWU_R, MULLI = 480, MULLW, MULLW_O, MULLW_O_R, MULLW_R, NAND = 493, NAND_R, NEG, NEG_O, NEG_O_R, NEG_R, NOP, NOR, NOR_R, OR_OP, OR_R, ORC, ORC_R, ORI, ORIS = 508, RFI = 512, RLWIMI = 522, RLWIMI_R, RLWINM, RLWINM_R, RLWNM, RLWNM_R, SC = 530, SLW = 566, SLW_R, SLWI, SLWI_R, SRAW = 580, SRAW_R, SRAWI, SRAWI_R, SRW = 600, SRW_R, SRWI, SRWI_R, STB = 605, STBU = 607, STBUX, STBX, STFD, STFDU, STFDUX, STFDX, STFIWX, STFS, STFSU, STFSUX, STFSX, STH, STHBRX, STHU, STHUX, STHX, STMW = 625, STSWI = 627, STSWX, STW = 632, STWBRX, STWCX_R, STWU, STWUX, STWX, SUBF = 639, SUBF_O, SUBF_O_R, SUBF_R, SUBFC, SUBFC_O, SUBFC_O_R, SUBFC_R, SUBFE, SUBFE_O, SUBFE_O_R, SUBFE_R, SUBFIC, SUBFME, SUBFME_O, SUBFME_O_R, SUBFME_R, SUBFZE, SUBFZE_O, SUBFZE_O_R, SUBFZE_R, SYNC = 664, TLBIE = 674, TLBLD, TLBLI, TLBSYNC, TRAP = 694, TRAPI, TW, TWEQ, TWEQI, TWGE, TWGEI, TWGT, TWGTI, TWI, TWLE, TWLEI, TWLGE, TWLGEI, TWLGT, TWLGTI, TWLLE, TWLLEI, TWLLT, TWLLTI, TWLNE, TWLNEI, TWLT, TWLTI, TWNE, TWNEI, XOR_OP, XOR_R, XORI, XORIS = 724, DATAB = 726, DATAH, DATAW }; enum REGISTER { /* General Purpose Registers. */ R0, R1, SP = R1, /* R1 = Stack pointer */ R2, /* R2 = TOC or SDA2 BASE */ R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, /* R13 = SDA BASE */ R14, /* R14 = Stack limit */ R15, R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, R26, R27, R28, R29, R30, R31, FP = R31, /* R31 = Frame pointer */ /* Floating Point Registers. */ FR0, FR1, FR2, FR3, FR4, FR5, FR6, FR7, FR8, FR9, FR10, FR11, FR12, FR13, FR14, FR15, FR16, FR17, FR18, FR19, FR20, FR21, FR22, FR23, FR24, FR25, FR26, FR27, FR28, FR29, FR30, FR31, /* Condition Register Fields. */ CR0, CR1, CR2, CR3, CR4, CR5, CR6, CR7, /* Floating Point Condition Register Fields. */ FCR0, FCR1, FCR2, FCR3, FCR4, FCR5, FCR6, FCR7, /* STORAGE CONTROL REGISTERS. */ SR0, SR1, SR2, SR3, SR4, SR5, SR6, SR7, SR8, SR9, SR10, SR11, SR12, SR13, SR14, SR15, /* Special Purpose Registers. */ SPR1 = 97, XER = SPR1, SPR4 = 98, RTCU = SPR4, SPR20 = 99, SPR5 = 100, RTCL = SPR5, SPR21 = 101, SPR22 = 102, DEC = SPR22, SPR8 = 103, LR = SPR8, SPR9 = 104, CTR = SPR9, SPR18 = 105, DSISR = SPR18, SPR19 = 106, DAR = SPR19, SPR25 = 109, SDR1 = SPR25, SPR26 = 110, SRR0 = SPR26, SPR27 = 111, SRR1 = SPR27, SPR272 = 112, SPRG0 = SPR272, SPR273 = 113, SPRG1 = SPR273, SPR274 = 114, SPRG2 = SPR274, SPR275 = 115, SPRG3 = SPR275, SPR282 = 116, EAR = SPR282, SPR284 = 117, TBL = SPR284, TBR268 = 118, SPR285 = 119, TBU = SPR285, TBR269 = 120, SPR287 = 121, PVR = SPR287, SPR528 = 122, IBAT0U = SPR528, SPR529 = 123, IBAT0L = SPR529, SPR530 = 124, IBAT1U = SPR530, SPR531 = 125, IBAT1L = SPR531, SPR532 = 126, IBAT2U = SPR532, SPR533 = 127, IBAT2L = SPR533, SPR534 = 128, IBAT3U = SPR534, SPR535 = 129, IBAT3L = SPR535, SPR536 = 130, DBAT0U = SPR536, SPR537 = 131, DBAT0L = SPR537, SPR538 = 132, DBAT1U = SPR538, SPR539 = 133, DBAT1L = SPR539, SPR540 = 134, DBAT2U = SPR540, SPR541 = 135, DBAT2L = SPR541, SPR542 = 136, DBAT3U = SPR542, SPR543 = 137, DBAT3L = SPR543, SPR952 = 138, MMCR0 = SPR952, SPR953 = 139, PMC1 = SPR953, SPR954 = 140, PMC2 = SPR954, SPR955 = 141, SIA = SPR955, SPR959 = 142, SDA = SPR959, SPR976 = 143, DMISS = SPR976, SPR977 = 144, DCMP = SPR977, SPR978 = 145, HASH1 = SPR978, SPR979 = 146, HASH2 = SPR979, SPR980 = 147, IMISS = SPR980, SPR981 = 148, ICMP = SPR981, SPR982 = 149, RPA = SPR982, SPR1008 = 150, HID0 = SPR1008, SPR1009 = 151, HID1 = SPR1009, SPR1010 = 152, HID2 = SPR1010, IABR = SPR1010, SPR1013 = 153, HID5 = SPR1013, DABR = SPR1013, SPR1023 = 154, HID15 = SPR1023, PIR = SPR1023 }; /* ** Addressing modes. ** ** ** Forms: ** reg + const ** reg + label ** reg + ext ** reg + code ** reg - const */ # ifdef __cplusplus enum OPERAND {}; typedef int SPACE_ID; /* 0..3 */ typedef REGISTER SPACE_REGISTER; /* SR0..SR7 */ typedef int DISPLACEMENT; typedef char* STRING; typedef int* LABEL; extern OPERAND ADDR(DISPLACEMENT); extern OPERAND ADDR(REGISTER); extern OPERAND ADDR(OPERAND); extern OPERAND DIFF(LABEL, LABEL); extern OPERAND EXT(STRING); extern OPERAND EXT(STRING, DISPLACEMENT); extern OPERAND SUBP_EXT(STRING); //extern OPERAND operator+(REGISTER, REGISTER); //extern OPERAND operator+(REGISTER, DISPLACEMENT); //extern OPERAND operator+(REGISTER, LABEL); //extern OPERAND operator-(REGISTER, DISPLACEMENT); # else enum OPERAND {_EMPTY_}; extern ADDR(); extern DIFF(); extern EXT(); extern SUBP_EXT(); #pragma builtin_asm ADDR; #pragma builtin_asm DIFF; #pragma builtin_asm EXT; #pragma builtin_asm SUBP_EXT; # endif #endif
Rational Software Corporation http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2002, Rational Software Corporation. All rights reserved. |