DS
和附加段寄存器ES
DS
段内待显示字符串的首地址,即标号"message"所代表的汇编地址CX
寄存器DS:SI
取得第一个字符ES:DI
即显示缓冲区SI
,DI
加一指向原位置和目标位置的下一个单元CX
的内容减一,然后根据CX
是否为零决定是否循环,CX
为零时,显示完毕。AX清零,1传到寄存器CX
,开始累加。CX
同100比较
AX中得到累加和。
得到累加和,要把各数位分解出来,并准备在屏幕上显示。
保存在栈中,栈段,由段寄存器SS指向。
需要栈指针寄存器SP
来指示下一个数据应当压入栈内的什么位置,或者数据从哪里出栈
40~42行初始化SS
和SP
的内容
44行 除数传送到BX
每次除法结束后,都做一次判断,如果商为0的话,分解过程可以提前结束,必须记住实际有多少个数位。45行CX
寄存器清零,用来累计
47到53行也是循环体,每次执行,分解一个数位。每次分解CX
加一,表明多一个数位
48,49行,将DX
清零,和AX
一起形成32的被除数。
50行,or不是真正的加法。这是因为每次是除以10,所以DL
中得到的余数,高四位必定是0x30低四位是0,高4位是3。所以or之后是相当于加
51行,push指令将DX
内容压入栈中。push指令在8086中只能压入一个字,但其后的32位64位处理器允许压入字,双字,四字。因此必须有关键字。
执行push指令,首先将SP的内容减去操作数的字长,然后把要压入栈的数据放到逻辑地址SS:SP
所在位置。
SP-2即0x0000-0x0002=0xFFFE,错位被忽略。Intel使用低端字节序,低字节在低地址,高字节在高地址。压栈操作是从高地址端向低地址短推进。
57行,pop弹出到DX
中,然后将SP的内容加上操作数的字长
执行完最后一次出栈,SP的内容重新为0
引入栈和push、pop只是为了方便程序开发。临时保存一个数值到栈中使用push指令是最简洁最省事的。否则push ax 可为
sub sp,2
mov bx,sp
mov [ss:bx],ax
pop ax类似
mov bx,sp
mox ax,[ss:bx]
add sp,2
如果你想把临时数据都保存在数据段,那么你必须在数据段中开辟一些空间,并亲自维护一个指针来跟综这些数据的存入和取出。
保持栈的平衡,SP在做事之前的值和之后的值要相同,即push和pop数相等。
必须充分估计需要的栈空间,如果栈段和代码段属于同一内存段,栈定义得过小,程序编写不当,栈可能破坏有用数据。
请举例说明为什么在压栈操作中,栈指针需要先减去一个偏移量,然后再将数据存储到栈中在出栈操作中,在出栈操作中,栈指针需要先访问栈中的数据,然后再将栈指针向上移动一个偏移量。
假设我们有一个栈,初始时栈指针 SP 指向栈底的内存地址,栈中已经存储了两个元素。此时我们要进行一次压栈操作和一次出栈操作,来说明为什么在这两个操作中需要先进行偏移量处理。
假设要压入的数据为 0x1234,大小为 2 字节,压栈操作流程如下:
此时栈的情况如下:
栈顶 | 0x1234 |
---|---|
元素2 | 0x5678 |
元素1 | 0x9abc |
---|---|
栈底 | SP指向 |
接下来,我们进行一次出栈操作,将栈顶的元素弹出,出栈操作流程如下:
此时栈的情况如下:
栈顶 | 0x5678 |
---|---|
元素2 | 0x5678 |
元素1 | 0x9abc |
---|---|
栈底 | SP指向 |
可以看到,在进行压栈和出栈操作时,都需要先进行偏移量处理,这样才能保证栈指针的正确性和栈中数据的一致性。
Bochs中察看栈的命令是"print-stack",默认显示当前栈中的16个字
寻址方式就是如何找到要操作的数据,以及如何找到存放操作结果的地方(基于16位处理器)
mov ax,cx
add bx,0xf000
inc dx
add bx,0xf000
mov dx,label_a
标号在编译阶段转化为立即数
立即寻址的操作数位于指令中,是指令的一部分
mov ax,[0x5c0f]
add word [0x0230],0x5000
xor byte [es:label_b],0x05
BX
或BP
来提供偏移地址mov [bx],dx //将DS的内容左移4位,加上基址寄存器BX的内容
add byte [bx-2],0x55 //可加减一个偏移量不改变bx内容
mov ax,[bp] //该寄存器的默认段寄存器是SS,常用于访问栈
高级语言里的函数调用,所有的参数都在栈中。为了能访问到那些被压在栈底的参数,就需要用到BP
。
将 string db 'abcdef到z’原地反向排列
mov bx,string
mov si,0
mov di,25
order:
mov ah,[bx+si]
mov al,[bx+di]
mov [bx+si],al
mov [bx+di],ah
inc si
dec di
cmp si,di
jl order
本章汇编:
;代码清单7-1
;文件名:c07_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-4-13 18:02
00000000 E90E00 jmp near start
00000003 312B322B332B2E2E2E- message db '1+2+3+...+100='
0000000C 2B3130303D
start:
00000011 B8C007 mov ax,0x7c0 ;设置数据段的段基地址
00000014 8ED8 mov ds,ax
00000016 B800B8 mov ax,0xb800 ;设置附加段基址到显示缓冲区
00000019 8EC0 mov es,ax
;以下显示字符串
0000001B BE[0300] mov si,message
0000001E BF0000 mov di,0
00000021 B90E00 mov cx,start-message
@g:
00000024 8A04 mov al,[si]
00000026 268805 mov [es:di],al
00000029 47 inc di
0000002A 26C60507 mov byte [es:di],0x07
0000002E 47 inc di
0000002F 46 inc si
00000030 E2F2 loop @g
;以下计算1到100的和
00000032 31C0 xor ax,ax
00000034 B90100 mov cx,1
@f:
00000037 01C8 add ax,cx
00000039 41 inc cx
0000003A 83F964 cmp cx,100
0000003D 7EF8 jle @f
;以下计算累加和的每个数位
0000003F 31C9 xor cx,cx ;设置堆栈段的段基地址
00000041 8ED1 mov ss,cx
00000043 89CC mov sp,cx
00000045 BB0A00 mov bx,10
00000048 31C9 xor cx,cx
@d:
0000004A 41 inc cx
0000004B 31D2 xor dx,dx
0000004D F7F3 div bx
0000004F 80CA30 or dl,0x30
00000052 52 push dx
00000053 83F800 cmp ax,0
00000056 75F2 jne @d
;以下显示各个数位
@a:
00000058 5A pop dx
00000059 268815 mov [es:di],dl
0000005C 47 inc di
0000005D 26C60507 mov byte [es:di],0x07
00000061 47 inc di
00000062 E2F4 loop @a
00000064 E9FDFF jmp near $
00000067 00 times 510-($-$$) db 0
000001FE 55AA db 0x55,0xaa