欢迎来到皮皮网网站!

【乐檬4.5源码】【开心川麻源码】【unity漫游工程源码】Arm __main源码

时间:2024-11-30 02:37:21 来源:ym源码x站源码

1.linuxuboot
2.FreeRTOSv202212.01移植到STM32
3.代码 | C语言根据可执行文件名获取进程运行信息
4.如何在C语言中嵌入汇编?

Arm __main源码

linuxuboot

       uboot是什么意思?

       U-Boot,全称UniversalBootLoader,是遵循GPL条款的开放源码项目。U-Boot的作用是系统引导。U-Boot从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是根据相应的Linux内核源程序进行简化而形成的,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。

       uboot是一个操作系统内核还是一段引导程序?

       æ˜¯ä¸€æ®µå¼•å¯¼ç¨‹åºã€‚

       1,uboot是用来干什么的,有什么作用?

       uboot属于bootloader的一种,是用来引导启动内核的,它的最终目的就是,从flash中读出内核,放到内存中,启动内核

       æ‰€ä»¥ï¼Œç”±ä¸Šé¢æè¿°çš„,就知道,UBOOT需要具有读写flash的能力。

       2,uboot是怎样引导启动内核的?

       uboot刚开始被放到flash中,板子上电后,会自动把其中的一部分代码拷到内存中执行,这部分代码负责把剩余的uboot代码拷到内存中,然后uboot代码再把kernel部分代码也拷到内存中,并且启动,内核启动后,挂着根文件系统,执行应用程序。

       3,uboot启动的大过程是怎么样的?

       uboot启动主要分为两个阶段,主要在start.s文件中,第一阶段主要做的是硬件的初始化,包括,设置处理器模式为SVC模式,关闭看门狗,屏蔽中断,初始化sdram,设置栈,设置时钟,从flash拷贝代码到内存,清除bss段等,bss段是用来存储静态变量,全局变量的,然后程序跳转到start_arm_boot函数,宣告第一阶段的结束。

       ç¬¬äºŒé˜¶æ®µæ¯”较复杂,做的工作主要是1.从flash中读出内核。2.启动内核。start_arm_boot的主要流程为,设置机器id,初始化flash,然后进入main_loop,等待uboot命令,uboot要启动内核,主要经过两个函数,第一个是s=getenv(bootcmd),第二个是run_command(s...),所以要启动内核,需要根据bootcmd环境变量的内容启动,bootcmd环境变量一般指示了从某个flash地址读取内核到启动的内存地址,然后启动,bootm。

       uboot启动的内核为uImage,这种格式的内核是由两部分组成:真正的内核和内核头部组成,头部中包括内核中的一些信息,比如内核的加载地址,入口地址。

       uboot在接受到启动命令后,要做的主要是,1,读取内核头部,2,移动内核到合适的加载地址,3,启动内核,执行do_bootm_linux

       do_bootm_linux主要做的为,1,设置启动参数,在特定的地址,保存启动参数,函数分别为setup_start_tag,setup_memory_tag,setup_commandline_tag,setup_end_tag,根据名字我们就知道具体的段内存储的信息,memory中为板子的内存大小信息,commandline为命令行信息,

       2,跳到入口地址,启动内核

       å¯åŠ¨çš„函数为the_kernel(0,bd->bi_arch_number,bd->bi_boot_param)

       bd->bi_arch_number为板子的机器码,bd->bi_boot_param为启动参数的地址

       æ€»ç»“:

       1)uboot主要作用是用来启动操作系统内核。体现在uboot最后一句代码就是启动内核。

       2)uboot还要负责部署整个计算机系统。体现在uboot最后的传参。

       3)uboot中还有操作Flash等板子上硬件的驱动。例如串口要打印,ping网络成功,擦除、烧写flash是否成功等。

       4)uboot还得提供一个命令行界面供人来操作。很简单,至少你能看到。

       uboot命令如何引导linux?

       U-Boot除了Bootloader的系统引导功能,它还有用户命令接口,具备多种引导内核启动的方式。常用的go和bootm命令可以直接引导Linux内核映像启动。U-Boot,全称UniversalBootLoader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是根据相应的Linux内核源程序进行简化而形成的,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。

       å¦‚何ghostlinux系统?

       ä½¿ç”¨Ghost软件来实现备份和恢复Linux系统:

       1、首先用“雨林木风GhostXPSP3纯净版Y6.0”光盘进行引导,并使用其中的PQ软件对硬盘进行分区,划分出一个分区用于安装DOS系统。然后选择:“运行YLMFDOS工具箱”。即:运行PQ软件,对硬盘进行分区。

       2、先分一个分区,大小为大约为4G,用于安装DOS系统以及存放GHOST所做的备份文件,当然实际当中,应该将这个分区的大小调大点。

       3、同时这里把新分区出来的C盘,设置为”活动“,以方便启动时,能从C盘启动DOS系统。

       4、用光盘进行引导,创建C盘启动DOS系统时所需要的文件,以及GHOST程序等复制到C盘。

       5、安装Linux系统,在安装时,建议不要把GRUB安装MBR上,同时在划分分区时,建议为:boot、var、home创建单独的分区。

       6、从DOS系统启动,然后运行GHOST软件对Linux系统进行备份。1)、备份Linux系统的boot分区(“/boot")。2)、备份Linux系统的根分区("/")7、Linux系统出现问题,无法启动注意:由于之前把DOS系统的分区(即:C盘),设置成“活动”,所以这里必须修改一下,让“/boot"设置成活动的,这样才能启动linux系统。

FreeRTOSv.移植到STM

       介绍

       将FreeRTOSv.移植到STMFC8T6单片机的过程概述。此版本为当前最新,适用于开发者进行系统级编程。

       硬件平台

       选用STMFC8T6作为移植目标,此型号单片机提供丰富的外设资源,适合各种嵌入式应用。乐檬4.5源码

       软件架构与操作步骤

       1. 从freertos.org/zh-cn-cmn下载源代码。注意,V9.0以后版本由Amazon收购后开发,V9.0被认为较为稳定。本文档介绍的移植方法适用于最新版本,与旧版本相比,源代码变化不大,可进行对比学习。

       2. 解压源码并复制FreeRTOS目录内容至项目目录下。删除除Source文件夹外的所有文件,Source文件夹是核心模块,包含实际移植所需的文件。

       3. 进入Source文件夹,删除非源代码文件,保留include和portable目录,前者包括头文件,后者为接口文件。

       4. 进入Source下的portable文件夹,删除非必要的文件和文件夹(如RVDS和MemMang),仅保留与M3内核相关的ARM_CM3接口文件以及heap_4.c,其余可保留但暂不使用。

       5. 从FreeRTOSv.\FreeRTOS\Demo\CORTEX_STMF_Keil复制FreeRTOSConfig.h至include文件夹。此配置文件用于系统裁剪。

       6. 将保留的文件添加到工程中,并配置文件路径。确保编译无错误。

       创建任务的详细步骤

       在main.c文件中,创建任务。关注stmfx_it.h文件,增加xPortSysTickHandler外部声明,并注释掉SVC_Handler和PendSV_Handler。开心川麻源码SysTick_Handler作为定时器回调函数,用于调度处理。

       在FreeRTOSConfig.h中添加中断宏定义,由port.c中的汇编语言实现,用于任务启动和切换。设置INCLUDE_xTaskGetSchedulerState为1。

       在main.h中加入RTOS头文件,包含操作系统所需的所有调用文件。

       至此,重新编译即可运行程序。最后,确保工程总目录包含标准库stdlib(3.5.0版本),以支持完整的硬件抽象层。

代码 | C语言根据可执行文件名获取进程运行信息

       通过C语言,根据可执行文件名获取进程运行信息的代码示例如下。本文提供3年嵌入式物联网学习资源,包括C语言、Linux开发、数据结构等内容,以及软件开发、STM单片机、ARM硬件开发、物联网通信开发、综合项目开发教程资料,同时附带笔试面试真题。

       以下是程序源码的主要部分:

       main.c文件:

       proc_info.c文件:

       proc_info.h文件:

       进行编译时,由于使用了线程功能,需要链接pthread库。

       测试结果显示,通过此代码,可以获取到线程数、虚拟内存占用大小、物理内存占用大小、进程PID、CPU占用率和进程启动次数等信息。

       原文链接:mp.weixin.qq.com/s/oXlv...

       转载自:嵌入式大杂烩

       文章来源于:blog.csdn.net/fangye...

       原文链接:代码 | C语言根据可执行文件名获取进程运行信息

       版权声明:本文源自网络,unity漫游工程源码旨在免费分享知识,尊重原作者版权,如涉及作品版权问题,请联系删除。

如何在C语言中嵌入汇编?

       在 Visual C++ 中使用内联汇编- -

       使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤。在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工具。这里,我们就以 Visual Studio .NET 为背景,介绍在 Visual C++ 中使用内联汇的相关知识(如果是早期的版本,可能会有些许出入)。

        内联汇编代码可以使用 C/C++ 变量和函数,因此它能非常容易地整合到 C/C++ 代码中。它能做一些对于单独使用 C/C++ 来说非常笨重或不可能完成的任务。

       一、 优点

        使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤。在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工具。这里,我们就以 Visual Studio .NET 为背景,介绍在 Visual C++ 中使用内联汇的相关知识(如果是早期的版本,可能会有些许出入)。

        内联汇编代码可以使用 C/C++ 变量和函数,因此它能非常容易地整合到 C/C++ 代码中。它能做一些对于单独使用 C/C++ 来说非常笨重或不可能完成的任务。

        内联汇编的用途包括:

       使用汇编语言编写特定的函数;

       编写对速度要求非常较高的代码;

       在设备驱动程序中直接访问硬件;

       编写 naked 函数的初始化和结束代码。

       二、 关键字

        使用内联汇编要用到 __asm 关键字,它可以出现在任何允许 C/C++ 语句出现的地方。我们来看一些例子:

       简单的 __asm 块:

        __asm

        {

        MOV AL, 2

        MOV DX, 0xD

        OUT AL, DX

        }

       在每条汇编指令之前加 __asm 关键字:

        __asm MOV AL, 2

        __asm MOV DX, 0xD

        __asm OUT AL, DX

       因为 __asm 关键字是语句分隔符,所以可以把多条汇编指令放在同一行:

        __asm MOV AL, 2 __asm MOV DX, 0xD __asm OUT AL, DX

        显然,第一种方法与 C/C++ 的合成类app源码风格很一致,并且把汇编代码和 C/C++ 代码清楚地分开,还避免了重复输入 __asm 关键字,因此推荐使用第一种方法。

        不像在 C/C++ 中的"{ }",__asm 块的"{ }"不会影响 C/C++ 变量的作用范围。同时,__asm 块可以嵌套,而且嵌套也不会影响变量的作用范围。

        为了与低版本的 Visual C++ 兼容,_asm 和 __asm 具有相同的意义。另外,Visual C++ 支持标准 C++ 的 asm 关键字,但是它不会生成任何指令,它的作用仅限于使编译器不会出现编译错误。要使用内联汇编,必须使用 __asm 而不是 asm 关键字。

       三、 汇编语言

       1. 指令集

        内联汇编支持 Intel Pentium 4 和 AMD Athlon 的所有指令。更多其它处理器的指令可以通过 _EMIT 伪指令来创建(_EMIT 伪指令说明见下文)。

       2. MASM 表达式

        在内联汇编代码中,可以使用所有的 MASM 表达式(MASM 表达式是指用来计算一个数值或一个地址的操作符和操作数的组合)。

       3. 数据指示符和操作符

        虽然 __asm 块中允许使用 C/C++ 的数据类型和对象,但它不能使用 MASM 指示符和操作符来定义数据对象。这里特别指出,__asm 块中不允许 MASM 中的定义指示符(DB、DW、DD、DQ、DT 和 DF),也不允许使用 DUP 和 THIS 操作符。MASM 中的结构和记录也不再有效,内联汇编不接受 STRUC、RECORD、WIDTH 或者 MASK。

       4. EVEN 和 ALIGN 指示符

        尽管内联汇编不支持大多数 MASM 指示符,但它支持 EVEN 和 ALIGN。影视app源码缓存当需要的时候,这些指示符在汇编代码里面加入 NOP 指令(空操作)使标号对齐到特定边界。这样可以使某些处理器取指令时具有更高的效率。

       5. MASM 宏指示符

        内联汇编不是宏汇编,不能使用 MASM 宏指示符(MACRO、REPT、IRC、IRP 和 ENDM)和宏操作符(<>、!、&、% 和 .TYPE)。

       6. 段

        必须使用寄存器而不是名称来指明段(段名称"_TEXT"是无效的)。并且,段跨越必须显式地说明,如 ES:[EBX]。

       7. 类型和变量大小

        在内联汇编中,可以用 LENGTH、SIZE 和 TYPE 来获取 C/C++ 变量和类型的大大小。

        * LENGTH 操作符用来取得 C/C++ 中数组的元素个数(如果不是一个数组,则结果为 1)。

        * SIZE 操作符可以获取 C/C++ 变量的大小(一个变量的大小是 LENGTH 和 TYPE 的乘积)。

        * TYPE 操作符可以返回 C/C++ 类型和变量的大小(如果变量是一个数组,它得到的是数组中单个元素的大小)。

        例如,程序中定义了一个 8 维的整数型变量:

        int iArray[8];

        下面是 C 和汇编表达式中得到的 iArray 及其元素的相关值:

        __asm C Size

        LENGTH iArray sizeof(iArray)/sizeof(iArray[0]) 8

        SIZE iArray sizeof(iArray)

        TYPE iArray sizeof(iArray[0]) 4

       8. 注释

        内联汇编中可以使用汇编语言的注释,即";"。例如:

        __asm MOV EAX, OFFSET pbBuff ; Load address of pbBuff

        因为 C/C++ 宏将会展开到一个逻辑行中,为了避免在宏中使用汇编语言注释带来的混乱,内联汇编也允许使用 C/C++ 风格的注释。

       9. _EMIT 伪指令

        _EMIT 伪指令相当于 MASM 中的 DB,但是 _EMIT 一次只能在当前代码段(.text 段)中定义一个字节。例如:

        __asm

        {

        JMP _CodeLabel

        _EMIT 0x ; 定义混合在代码段的数据

        _EMIT 0x

        _CodeLabel: ; 这里是代码

        _EMIT 0x ; NOP指令

        }

       . 寄存器使用

        一般来说,不能假定某个寄存器在 __asm 块开始的时候有已知的值。寄存器的值将不能保证会从 __asm 块保留到另外一个 __asm 块中。

        如果一个函数声明为 __fastcall 调用方式,则其参数将通过寄存器而不是堆栈来传递。这将会使 __asm 块产生问题,因为函数无法被告知哪个参数在哪个寄存器中。如果函数接收了 EAX 中的参数并立即储存一个值到 EAX 中的话,原来的参数将丢失掉。另外,在所有声明为 __fastcall 的函数中,ECX 寄存器是必须一直保留的。为了避免以上的冲突,包含 __asm 块的函数不要声明为 __fastcall 调用方式。

       提示:如果使用 EAX、EBX、ECX、EDX、ESI 和 EDI 寄存器,你不需要保存它。但如果你用到了 DS、SS、SP、BP 和标志寄存器,那就应该用 PUSH 保存这些寄存器。

       提示:如果程序中改变了用于 STD 和 CLD 的方向标志,必须将其恢复到原来的值。

       四、 使用 C/C++ 元素

       1. 可用的 C/C++ 元素

        C/C++ 与汇编语言可以混合使用,在内联汇编中可以使用 C/C++ 变量以及很多其它的 C/C++ 元素,包括:

       符号,包括标号、变量和函数名;

       常量,包括符号常量和枚举型成员;

       宏定义和预处理指示符;

       注释,包括"/**/"和"//";

       类型名,包括所有 MASM 中合法的类型;

       typedef 名称,通常使用 PTR 和 TYPE 操作符,或者使用指定的的结构或枚举成员。

        在内联汇编中,可以使用 C/C++ 或汇编语言的基数计数法。例如,0x 和 H 是相等的。

       2. 操作符使用

        内联汇编中不能使用诸如"<<"一类的 C/C++ 操作符。但是,C/C++ 和 MASM 共有的操作符(比如"*"和"[]"操作符),都被认为是汇编语言的操作符,是可以使用的。举个例子:

        int iArray[];

        __asm MOV iArray[6], BX ; Store BX at iArray + 6 (Not scaled)

        iArray[6] = 0; // Store 0 at iArray+ (Scaled)

       提示:在内联汇编中,可以使用 TYPE 操作符使其与 C/C++ 一致。比如,下面两条语句是一样的:

        __asm MOV iArray[6 * TYPE int], 0 ; Store 0 at iArray +

        iArray[6] = 0; // Store 0 at iArray +

       3. C/C++ 符号使用

        在 __asm 块中可以引用所有在作用范围内的 C/C++ 符号,包括变量名称、函数名称和标号。但是不能访问 C++ 类的成员函数。

        下面是在内联汇编中使用 C/C++ 符号的一些限制:

       每条汇编语句只能包含一个 C/C++ 符号。在一条汇编指令中,多个符号只能出现在 LENGTH、TYPE 或 SIZE 表达式中。

       在 __asm 块中引用函数必须先声明。否则,编译器将不能区别 __asm 块中的函数名和标号。

       在 __asm 块中不能使用对于 MASM 来说是保留字的 C/C++ 符号(不区分大小写)。MASM 保留字包含指令名称(如 PUSH)和寄存器名称(如 ESI)等。

       在 __asm 块中不能识别结构和联合标签。

       4. 访问 C/C++ 中的数据

        内联汇编的一个非常大的方便之处是它可以使用名称来引用 C/C++ 变量。例如,如果 C/C++ 变量 iVar 在作用范围内:

        __asm MOV EAX, iVar ; Stores the value of iVar in EAX

        如果 C/C++ 中的类、结构或者枚举成员具有唯一的名称,则在 __asm 块中可以只通过成员名称来访问(省略"."操作符之前的变量名或 typedef 名称)。然而,如果成员不是唯一的,你必须在"."操作符之前加上变量名或 typedef 名称。例如,下面的两个结构都具有 SameName 这个成员变量:

        struct FIRST_TYPE

        {

        char *pszWeasel;

        int SameName;

        };

        struct SECOND_TYPE

        {

        int iWonton;

        long SameName;

        };

        如果按下面方式声明变量:

        struct FIRST_TYPE ftTest;

        struct SECOND_TYPE stTemp;

        那么,所有引用 SameName 成员的地方都必须使用变量名,因为 SameName 不是唯一的。另外,由于上面的 pszWeasel 变量具有唯一的名称,你可以仅仅使用它的成员名称来引用它:

        __asm

        {

        MOV EBX, OFFSET ftTest

        MOV ECX, [EBX]ftTest.SameName ; 必须使用"ftTest"

        MOV ESI, [EBX]. pszWeasel ; 可以省略"ftTest"

        }

       提示:省略变量名仅仅是为了书写代码方便,生成的汇编指令还是一样的。

       5. 用内联汇编写函数

        如果用内联汇编写函数的话,要传递参数和返回一个值都是非常容易的。看下面的例子,比较一下用独立汇编和内联汇编写的函数:

        ; PowerAsm.asm

        ; Compute the power of an integer

        PUBLIC GetPowerAsm

        _TEXT SEGMENT WORD PUBLIC 'CODE'

        GetPowerAsm PROC

        PUSH EBP ; Save EBP

        MOV EBP, ESP ; Move ESP into EBP so we can refer

        ; to arguments on the stack

        MOV EAX, [EBP+4] ; Get first argument

        MOV ECX, [EBP+6] ; Get second argument

        SHL EAX, CL ; EAX = EAX * (2 ^ CL)

        POP EBP ; Restore EBP

        RET ; Return with sum in EAX

        GetPowerAsm ENDP

        _TEXT ENDS

        END

        C/C++ 函数一般用堆栈来传递参数,所以上面的函数中需要通过堆栈位置来访问它的参数(在 MASM 或其它一些汇编工具中,也允许通过名称来访问堆栈参数和局部堆栈变量)。

        下面的程序是使用内联汇编写的:

        // PowerC.c

        #include

        int GetPowerC(int iNum, int iPower);

        int main()

        {

        printf("3 times 2 to the power of 5 is %d\n", GetPowerC( 3, 5));

        }

        int GetPowerC(int iNum, int iPower)

        {

        __asm

        {

        MOV EAX, iNum ; Get first argument

        MOV ECX, iPower ; Get second argument

        SHL EAX, CL ; EAX = EAX * (2 to the power of CL)

        }

        // Return with result in EAX

        }

        使用内联汇编写的 GetPowerC 函数可以通过参数名称来引用它的参数。由于 GetPowerC 函数没有执行 C 的 return 语句,所以编译器会给出一个警告信息,我们可以通过 #pragma warning 禁止生成这个警告。

        内联汇编的其中一个用途是编写 naked 函数的初始化和结束代码。对于一般的函数,编译器会自动帮我们生成函数的初始化(构建参数指针和分配局部变量等)和结束代码(平衡堆栈和返回一个值等)。使用内联汇编,我们可以自己编写干干净净的函数。当然,此时我们必须自己动手做一些有关函数初始化和扫尾的工作。例如:

        void __declspec(naked) MyNakedFunction()

        {

        // Naked functions must provide their own prolog.

        __asm

        {

        PUSH EBP

        MOV ESP, EBP

        SUB ESP, __LOCAL_SIZE

        }

        .

        .

        .

        // And we must provide epilog.

        __asm

        {

        POP EBP

        RET

        }

        }

       6. 调用 C/C++ 函数

        内联汇编中调用声明为 __cdecl 方式(默认)的 C/C++ 函数必须由调用者清除参数堆栈,下面是一个调用 C/C++ 函数例子:

        #include

        char szFormat[] = "%s %s\n";

        char szHello[] = "Hello";

        char szWorld[] = " world";

        void main()

        {

        __asm

        {

        MOV EAX, OFFSET szWorld

        PUSH EAX

        MOV EAX, OFFSET szHello

        PUSH EAX

        MOV EAX, OFFSET szFormat

        PUSH EAX

        CALL printf

        // 压入了 3 个参数在堆栈中,调用函数之后要调整堆栈

        ADD ESP,

        }

        }

       提示:参数是按从右往左的顺序压入堆栈的。

        如果调用 __stdcall 方式的函数,则不需要自己清除堆栈。因为这种函数的返回指令是 RET n,会自动清除堆栈。大多数 Windows API 函数均为 __stdcall 调用方式(仅除 wsprintf 等几个之外),下面是一个调用 MessageBox 函数的例子:

        #include

        TCHAR g_tszAppName[] = TEXT("API Test");

        void main()

        {

        TCHAR tszHello[] = TEXT("Hello, world!");

        __asm

        {

        PUSH MB_OK OR MB_ICONINFORMATION

        PUSH OFFSET g_tszAppName ; 全局变量用 OFFSET

        LEA EAX, tszHello ; 局部变量用 LEA

        PUSH EAX

        PUSH 0

        CALL DWORD PTR [MessageBox] ; 注意这里不是 CALL MessageBox,而是调用重定位过的函数地址

        }

        }

       提示:可以不受限制地访问 C++ 成员变量,但是不能访问 C++ 的成员函数。

       7. 定义 __asm 块为 C/C++ 宏

        使用 C/C++ 宏可以方便地把汇编代码插入到源代码中。但是这其中需要额外地注意,因为宏将会扩展到一个逻辑行中。

       为了不会出现问题,请按以下规则编写宏:

       使用花括号把 __asm 块包围住;

       把 __asm 关键字放在每条汇编指令之前;

       使用经典 C 风格的注释("/* comment */"),不要使用汇编风格的注释("; comment")或单行的 C/C++ 注释("// comment");

        举个例子,下面定义了一个简单的宏:

        #define PORTIO __asm \

        /* Port output */ \

        { \

        __asm MOV AL, 2 \

        __asm MOV DX, 0xD \

        __asm OUT DX, AL \

        }

        乍一看来,后面的三个 __asm 关键字好像是多余的。其实它们是需要的,因为宏将被扩展到一个单行中:

        __asm /* Port output */ { __asm MOV AL, 2 __asm MOV DX, 0xD __asm OUT DX, AL }

        从扩展后的代码中可以看出,第三个和第四个 __asm 关键字是必须的(作为语句分隔符)。在 __asm 块中,只有 __asm 关键字和换行符会被认为是语句分隔符,又因为定义为宏的一个语句块会被认为是一个逻辑行,所以必须在每条指令之前使用 __asm 关键字。

        括号也是需要的,如果省略了它,编译器将不知道汇编代码在哪里结束,__asm 块后面的 C/C++ 语句看起来会被认为是汇编指令。

        同样是由于宏展开的原因,汇编风格的注释("; comment")和单行的 C/C++ 注释("// commen")也可能会出现错误。为了避免这些错误,在定义 __asm 块为宏时请使用经典 C 风格的注释("/* comment */")。

        和 C/C++ 宏一样 __asm 块写的宏也可以拥有参数。和 C/C++ 宏不一样的是,__asm 宏不能返回一个值,因此,不能使用这种宏作为 C/C++ 表达式。

        不要不加选择地调用这种类型的宏。比如,在声明为 __fastcall 的函数中调用汇编语言宏可能会导致不可预料的结果(请参看前文的说明)。

       8. 转跳

        可以在 C/C++ 里面使用 goto 转跳到 __asm 块中的标号处,也可以在 __asm 块中转跳到 __asm 块里面或外面的标号处。__asm 块内的标号是不区分大小写的(指令、指示符等也是不区分大小写的)。例如:

        void MyFunction()

        {

        goto C_Dest; /* 正确 */

        goto c_dest; /* 错误 */

        goto A_Dest; /* 正确 */

        goto a_dest; /* 正确 */

        __asm

        {

        JMP C_Dest ; 正确

        JMP c_dest ; 错误

        JMP A_Dest ; 正确

        JMP a_dest ; 正确

        a_dest: ; __asm 标号

        }

        C_Dest: /* C/C++ 标号 */

        return;

        }

        不要使用函数名称当作标号,否则将转跳到函数中执行,而不是标号处。例如,由于 exit 是 C/C++ 的函数,下面的转跳将不会到 exit 标号处:

        ; 错误:使用函数名作为标号

        JNE exit

        .

        .

        .

        exit:

        .

        .

        .

        美元符号"$"用于指定当前指令位置,常用于条件跳转中,例如:

        JNE $+5 ; 下面这条指令的长度是 5 个字节

        JMP _Label

        NOP ; $+5,转跳到了这里

        .

        .

        .

        _Label:

        .

        .

        .

       五、在 Visual C++ 工程中使用独立汇编

        内联汇编代码不易于移植,如果你的程序打算在不同类型的机器(比如 x 和 Alpha)上运行,你可能需要在不同的模块中使用特定的机器代码。这时候你可以使用 MASM(Microsoft Macro Assembler),因为 MASM 支持更多方便的宏指令和数据指示符。

        这里简单介绍一下在 Visual Studio .NET 中调用 MASM 编译独立汇编文件的步骤。

        在 Visual C++ 工程中,添加按 MASM 的要求编写的 .asm 文件。在解决方案资源管理器中,右击这个文件,选择"属性"菜单项,在属性对话框中,点击"自定义生成步骤",设置如下项目:

        命令行:ML.exe /nologo /c /coff "-Fo$(IntDir)\$(InputName).obj" "$(InputPath)"

        输出:$(IntDir)\$(InputName).obj

        如果要生成调试信息,可以在命令行中加入"/Zi"参数,还可以根据需要生成 .lst 和 .sbr 文件。

        如果要在汇编文件中调用 Windows API,可以从网上下载 MASM 包(包含了 MASM 汇编工具、非常完整的 Windows API 头文件/库文件、实用宏以及大量的 Win 汇编例子等)。相应地,应该在命令行中加入"/I X:\MASM\INCLUDE"参数指定 Windows API 汇编头文件(.inc)的路径。MASM 的主页是:,里面可以下载最新版本的 MASM 包。

更多相关资讯请点击【探索】频道>>>