unsigned long
get_free_page (void)
{
register unsigned long __res asm ("ax");
__asm__ ("std ; repne ; scasb\n\t"
"jne 1f\n\t"
"movb $1,1(%%edi)\n\t"
"sall $12,%%ecx\n\t"
"addl %2,%%ecx\n\t"
"movl %%ecx,%%edx\n\t"
"movl $1024,%%ecx\n\t"
"leal 4092(%%edx),%%edi\n\t"
"rep ; stosl\n\t"
"movl %%edx,%%eax\n"
"1:": "=a" (__res): "" (0), "i" (LOW_MEM), "c" (PAGING_PAGES), "D" (mem_map + PAGING_PAGES - 1):"di", "cx",
"dx");
return __res;
}
函数作用分配一页(4K)。稍后嵌入式汇编的格式要稍作介绍一下,其实我也不是很熟悉,多练习吧。
代码分析:
1,:register unsigned long __res asm ("ax");
定义了一个名为__res的变量,使用了register修饰符(register修饰符暗示编译程序相应的变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度)。asm("ax")则表示__res的值保存在寄存器ax中。
2:"std ; repne ; scasb\n\t"
一下子来了三兄弟。std设置方向,先设置方向嘛!立场原则还是要有的(还有一个cld清除方向,指示di和si寄存器的移动方向;cld时fd=0,地址从低到高,std相反)。repne scasb联合使用,repne重复命令(按计数寄存器 ((E)CX) 中指定的次数重复执行字符串指令,或是重复到 ZF 标志不再满足指定的条件。REP(重复)、REPE(相等时重复)、REPNE(不相等时重复)、REPZ(为零时重复)及 REPNZ(不为零时重复)助记符都是可以添加到一些字符串指令中的前缀)。scasb命令比较增加,将al中的值域es:di比较,inc di或dec di(取决于DF)。
此步骤的操作变量位于”输入寄存器“。如果es:di的值不等于al则dec di,否则执行下一步。
3:"jne 1f\n\t"
如果es:di的值相均不和al相等,即均不为零。跳转到标签1f处执行,Nf表示向前标签,Nb表示向后标签,N是取值1-10的十进制数字
4:"movb $1,1(%%edi)\n\t"
找到未使用的页面置1,表示已经被分配。
5:"sall $12,%%ecx\n\t"
算法左移,相当于c中<<,intel汇编中sal命令,长整型的操作。此时ecx保存的是mem_map[i]的下标i,即相对页面数,
6:"addl %2,%%ecx\n\t"
加上低端内存的地址,即是页面的物理地址,%2即是第二个输入参数"i" (LOW_MEM)。
7:"movl %%ecx,%%edx\n\t"
将ecx寄存器的值存在edx中,即将实际物理地址存入edx中。
8:"movl $1024,%%ecx\n\t"
将1024存入ecx中,1k。一页4k,实际物理内存每项占4个字节,一共1024项。
9:"leal 4092(%%edx),%%edi\n\t"
即是edx+4092存入edi。按照4字节对齐,4096即是最后一项,4096 - 4 = 4092 = 1023*4 = (1024-1)*4
leal 8(%ebp),%eax 即把8+(%ebp)的地址送入寄存器%eax中,类似的还有其他方式:
传送a值到eax:movl a, %%eax
传送a地址到edi: movl $a, %%edi
movl与leal的区别:
lead s,d 将&s->d
movl s, d 将s->d
10:"rep ; stosl\n\t"
重复1024次,将1024项物理地址全部存入eax中,
11:"movl %%edx,%%eax\n"
将物理地址的其实页面存入eax,interl的EABI规则eax用来保存函数的输出值。
12:"1:"
标签1,用于"jne 1f\n\t"语句跳转返回0值。
具有输入和输出参数的嵌入式汇编语句的基本格式为:
asm("汇编语句"
:输出寄存器
:输入寄存器
:会被修改的寄存器);
常用寄存器说明
代码 | 说明 | 代码 | 说明 |
a | 使用寄存器eax | m | 使用内存地址 |
b | 使用寄存器ebx | o | 使用内存地址并可以加偏移值 |
c | 使用寄存器ecx | I | 使用常数0-31 |
d | 使用寄存器edx | J | 使用常数0-63 |
S | 使用esi | K | 使用常数0-255 |
D | 使用edi | L | 使用常数0-65535 |
q | 使用动态分配字节可寻址寄存器(eax,ebx,ecx或edx) | M | 使用常数0-3 |
r | 使用任意动态分配的寄存器 | N | 使用1字节常数(0-255) |
g | 使用通用有效的地址即可(eax,ebx,ecx,edx或内存变量) | O | 使用常数0-31 |
A | 使用eax与edx联合(64位) | = | 输出操作数,输出值将替换前值 |
+ | 表示操作数可读可写 | & | 早期汇编的操作数。表示在使用完操作数之前,内容会被修改 |