
首先要了解的是CPU要处理的数据是存在内存中的,当CPU要处理某些数据时首先要将数据从内存中读取到CPU的寄存器中。内存的结构是以8个位(bit)为一个单元,由许多这样的单元组成了内存。就像一栋大楼是有许多个房间组成,每个房间的空间就是8个位。而数据存在内存的某一个或某一段单元中,CPU要读取怎么才能从这么多单元中找到呢,就是靠内存地址。内存地址就像我们的门牌号。有了这个,只要告诉CPU地址是多少,CPU就会从这段地址中读取数据。我们将内存中的一个单元的地址编为1,那么只要按顺序就会有2,3,4,5。。。。等等每个内存都会有个明确的地址编号。寻址范围,就是指CPU所能查找到的最小地址和最大地址。假如CPU的地址线是8位的,那么用二进制表示就是1111
1111。也就是说CPU访问内存从最小的0000
0000这个单元,一直到1111
1111这个单元。这是二进制,转换成十进制就是255也就是说,CPU可以访问的内存地址编号从0~255这就是CPU的寻址范围。
你的串号我已经记下,采纳后我会帮你制作
作者 Bharata B Rao 将各个部分组合起来 如果您是 Linux 内核的开发人员 您会发现自己经常要对与体系结构高度相关的功能进行编码或优化代码路径 您很可能是通过将汇编语言指令插入到 C 语句的中间(又称为内联汇编的一种方法)来执行这些任务的 让我们看一下 Linux 中内联汇编的特定用法 (我们将讨论限制在 IA 汇编 )GNU 汇编程序简述让我们首先看一下 Linux 中使用的基本汇编程序语法 GCC(用于 Linux 的 GNU C 编译器)使用 AT&T 汇编语法 下面列出了这种语法的一些基本规则 (该列表肯定不完整 只包括了与内联汇编相关的那些规则 )寄存器命名寄存器名称有 % 前缀 即 如果必须使用 eax 它应该用作 %eax 源 *** 作数和目的 *** 作数的顺序在所有指令中 先是源 *** 作数 然后才是目的 *** 作数 这与将源 *** 作数放在目的 *** 作数之后的 Intel 语法不同 mov %eax %ebx transfers the contents of eax to ebx *** 作数大小根据 *** 作数是字节 (byte) 字 (word) 还是长型 (long) 指令的后缀可以是 b w 或 l 这并不是强制性的 GCC 会尝试通过读取 *** 作数来提供相应的后缀 但手工指定后缀可以改善代码的可读性 并可以消除编译器猜测不正确的可能性 movb %al %bl Byte move movw %ax %bx Word move movl %eax %ebx Longword move立即 *** 作数通过使用 $ 指定直接 *** 作数 movl $ xffff %eax will move the value of xffff into eax register 间接内存引用任何对内存的间接引用都是通过使用 ( ) 来完成的 movb (%esi) %al will transfer the byte in the memory pointed by esi into alregister内联汇编GCC 为内联汇编提供特殊结构 它具有以下格式 GCG 的 a 结构 a ( assembler template : output operands (optional) : input operands (optional) : list of clobbered registers (optional) ); 本例中 汇编程序模板由汇编指令组成 输入 *** 作数是充当指令输入 *** 作数使用的 C 表达式 输出 *** 作数是将对其执行汇编指令输出的 C 表达式 内联汇编的重要性体现在它能够灵活 *** 作 而且可以使其输出通过 C 变量显示出来 因为它具有这种能力 所以 a 可以用作汇编指令和包含它的 C 程序之间的接口 一个非常基本但很重要的区别在于简单内联汇编只包括指令 而扩展内联汇编包括 *** 作数 要说明这一点 考虑以下示例 内联汇编的基本要素{ int a= b; a ( movl % %%eax; movl %%eax % ; : =r (b) / output / : r (a) / input / : %eax ); / clobbered register /}在上例中 我们使用汇编指令使 b 的值等于 a 请注意以下几点 b 是输出 *** 作数 由 % 引用 a 是输入 *** 作数 由 % 引用 r 是 *** 作数的约束 它指定将变量 a 和 b 存储在寄存器中 请注意 输出 *** 作数约束应该带有一个约束修饰符 = 指定它是输出 *** 作数 要在 a 内使用寄存器 %eax %eax 的前面应该再加一个 % 换句话说就是 %%eax 因为 a 使用 % % 等来标识变量 任何带有一个 % 的数都看作是输入/输出 *** 作数 而不认为是寄存器 第三个冒号后的修饰寄存器 %eax 告诉将在 a 中修改 GCC %eax 的值 这样 GCC 就不使用该寄存器存储任何其它的值 movl % %%eax 将 a 的值移到 %eax 中 movl %%eax % 将 %eax 的内容移到 b 中 因为 b 被指定成输出 *** 作数 因此当 a 的执行完成后 它将反映出更新的值 换句话说 对 a 内 b 所做的更改将在 a 外反映出来 现在让我们更详细的了解每一项的含义 汇编程序模板汇编程序模板是一组插入到 C 程序中的汇编指令(可以是单个指令 也可以是一组指令) 每条指令都应该由双引号括起 或者整组指令应该由双引号括起 每条指令还应该用一个定界符结尾 有效的定界符为新行 (\n) 和分号 (;) \n 后可以跟一个 tab(\t) 作为格式化符号 增加 GCC 在汇编文件中生成的指令的可读性 指令通过数 % % 等来引用 C 表达式(指定为 *** 作数) 如果希望确保编译器不会在 a 内部优化指令 可以在 a 后使用关键字 volatile 如果程序必须与 ANSI C 兼容 则应该使用 __a __ 和 __volatile__ 而不是 a 和 volatile *** 作数C 表达式用作 a 内的汇编指令 *** 作数 在汇编指令通过对 C 程序的 C 表达式进行 *** 作来执行有意义的作业的情况下 *** 作数是内联汇编的主要特性 每个 *** 作数都由 *** 作数约束字符串指定 后面跟用括弧括起的 C 表达式 例如 constraint (C expression) *** 作数约束的主要功能是确定 *** 作数的寻址方式 可以在输入和输出部分中同时使用多个 *** 作数 每个 *** 作数由逗号分隔开 在汇编程序模板内部 *** 作数由数字引用 如果总共有 n 个 *** 作数(包括输入和输出) 那么第一个输出 *** 作数的编号为 逐项递增 最后那个输入 *** 作数的编号为 n 总 *** 作数的数目限制在 如果机器描述中任何指令模式中的最大 *** 作数数目大于 则使用后者作为限制 修饰寄存器列表如果 a 中的指令指的是硬件寄存器 可以告诉 GCC 我们将自己使用和修改它们 这样 GCC 就不会假设它装入到这些寄存器中的值是有效值 通常不需要将输入和输出寄存器列为 clobbered 因为 GCC 知道 a 使用它们(因为它们被明确指定为约束) 不过 如果指令使用任何其它的寄存器 无论是明确的还是隐含的(寄存器不在输入约束列表中出现 也不在输出约束列表中出现) 寄存器都必须被指定为修饰列表 修饰寄存器列在第三个冒号之后 其名称被指定为字符串 至于关键字 如果指令以某些不可预知且不明确的方式修改了内存 则可能将 memory 关键字添加到修饰寄存器列表中 这样就告诉 GCC 不要在不同指令之间将内存值高速缓存在寄存器中 *** 作数约束前面提到过 a 中的每个 *** 作数都应该由 *** 作数约束字符串描述 后面跟用括弧括起的 C 表达式 *** 作数约束主要是确定指令中 *** 作数的寻址方式 约束也可以指定 是否允许 *** 作数位于寄存器中 以及它可以包括在哪些种类的寄存器中 *** 作数是否可以是内存引用 以及在这种情况下使用哪些种类的地址 *** 作数是否可以是立即数 约束还要求两个 *** 作数匹配 常用约束在可用的 *** 作数约束中 只有一小部分是常用的 下面列出了这些约束以及简要描述 有关 *** 作数约束的完整列表 请参考 GCC 和 GAS 手册 寄存器 *** 作数约束 (r)使用这种约束指定 *** 作数时 它们存储在通用寄存器中 请看下例 a ( movl %%cr % \n : =r (cr val));这里 变量 cr val 保存在寄存器中 %cr 的值复制到寄存器上 cr val 的值从该寄存器更新到内存中 指定 r 约束时 GCC 可以将变量 cr val 保存在任何可用的 GPR 中 要指定寄存器 必须通过使用特定的寄存器约束直接指定寄存器名 a %eaxb %ebxc %ecxd %edxS %esiD %edi内存 *** 作数约束 (m)当 *** 作数位于内存中时 任何对它们执行的 *** 作都将在内存位置中直接发生 这与寄存器约束正好相反 后者先将值存储在要修改的寄存器中 然后将它写回内存位置中 但寄存器约束通常只在对于指令来说它们是绝对必需的 或者它们可以大大提高进程速度时使用 当需要在 a 内部更新 C 变量 而您又确实不希望使用寄存器来保存其值时 使用内存约束最为有效 例如 idtr 的值存储在内存位置 loc 中 ( sidt % \n : : m (loc));匹配(数字)约束在某些情况下 一个变量既要充当输入 *** 作数 也要充当输出 *** 作数 可以通过使用匹配约束在 a 中指定这种情况 a ( incl % : =a (var): (var));在匹配约束的示例中 寄存器 %eax 既用作输入变量 也用作输出变量 将 var 输入读取到 %eax 增加后将更新的 %eax 再次存储在 var 中 这里的 指定第 个输出变量相同的约束 即 它指定 var 的输出实例只应该存储在 %eax 中 该约束可以用于以下情况 输入从变量中读取 或者变量被修改后 修改写回到同一变量中 不需要将输入 *** 作数和输出 *** 作数的实例分开 使用匹配约束最重要的意义在于它们可以导致有效地使用可用寄存器 一般内联汇编用法示例以下示例通过各种不同的 *** 作数约束说明了用法 有如此多的约束以至于无法将它们一一列出 这里只列出了最经常使用的那些约束类型 a 和寄存器约束 r 让我们先看一下使用寄存器约束 r 的 a 我们的示例显示了 GCC 如何分配寄存器 以及它如何更 lishixinzhi/Article/program/Oracle/201311/17556
看你的程序代码是从哪个地址开始放了
程序刚开始PC的值是0000H,如果没有用ORG 伪指令指定代码存放的位置,则从0000H算起,比如
MOV A,@R0 ;PC=0000H(执行这条变语句后就变为00001H,下同)
;该指令为1字节指令
INC R1 ;PC=0001H
END
又如:
ORG 0030H
MAIN:ANL 9FH, #0BH ;PC=0030H
DEC R0 ;PC=0033H
END ;PC=0031H
[]是C/C++语言的运算符,它是一个双目运算符,即需要两个参数:1数组名;2索引值;其中索引值必须是整数,而数组名其实就是某个地址值的别名,数组名与指针变量并不完全等价,原因是:数组名在编译时直接用地址值替换,而指针变量则还要绕个弯,要到变量中将地址取出。所幸,这个细微而繁琐的替换工作由编译器代劳了。另外,所有在函数中定义的数组(包括参数列表中的),编译器在实现上都将其用指针实现,因此大多数情况下指针与数组名在使用方法上等价。关于[]运算符,pt[x]等价于(pt+x),因此texture[0]等价于(texture+0)=texture。如果你有兴趣的话可以查看(pt+x)及pt[x]的汇编代码,如果pt是局部变量的话,它们的汇编代码形式完全一样。
只需要记住,间接寻址中 *** 作数是数据的家庭住址,而不是数据本身。
(数据的)地址=指针=(所占有内存块的首)地址
常用"常量[寄存器+常量表达式]",即带有中括号与寄存器的形式出现:数据的地址=(地址)常量+寄存器值+常量表达式值;其中的“常量”通常就是定义的符号标志,其编译本质都是代表一个地址常量
%p:打印裸指针(raw pointer)
%pF可打印函数指针的函数名和偏移地址
%pf只打印函数指针的函数名,不打印偏移地址。
如
printk("%pf",func[0]->action); 结果:
my_Set
%pM打印冒号分隔的MAC地址
%pm打印MAC地址的16进制无分隔
如
printk("%pM %pm\n", mac, mac) willprint:
2c:00:1d:00:1b:00 2c001d001b00
%I4打印无前导0的IPv4地址,%i4打印冒号分隔的IPv4地址
%I6打印无前导0的IPv6地址,%i6打印冒号分隔的IPv6地址
如
printk("%pI4 %pi4\n", ip, ip) will print:
127001 127:0:0:1
以上就是关于汇编语言内存单元地址的算法全部的内容,包括:汇编语言内存单元地址的算法、Linux 中 x86 的内联汇编、在汇编语言写单片机的程序的时候,如何知道当前的PC指针是多少等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)