可以来看一段汇编源程序
assume cs:codesg
codesg segment
mov ax,0123h
mov bx,0456h
add ax,bx
add ax,ax
mov ax,4c00h
int 21h
codesg ends
end
C++开头和结尾的两句代表伪指令 只有编译器可以读懂 汇编指令可以被翻译为机器码最终被cpu执行
汇编程序 就是包含汇编指令和伪指令的文本
mov ax,4c00h
int 21h
跟C语言程序的return 0一样 返回控制权
一个汇编程序是由多个段组成的 这些段被用作各种空间来使用 一个有意义的汇编程序至少需要一个段 且每个段都需要段名
段名 segment--段的开始
段名 ends--段的结束
assume假设
含义是假设某一段寄存器和程序中我们定义的段名关联起来 可以理解为和变量的引用一个意思
利用汇编程序计算2^3
assume cs:code
code segment
mov ax,2
add ax,ax
add ax,ax
code ends
end
C++汇编程序的编写不是我学习的重点,这里直接跳过 需要的可以自己去看一下
[…]的规定与(…)的约定
[]表示一个内存单元 段地址在ds寄存器 偏移地址在方括号里面 操作单位看对应的指令操作符
()表示一个内存单元或寄存器中的内容
(ax)表示ax的内容 (21000h)表示内存单元2000:1000处的内容
push ax (sp) = (sp)-2
((ss)*16+(sp)) = (ax)
pop ax (ax) = ((ss)*16+(sp))
loop指令
功能:实现循环(计数型循环)
计数通过判断cx寄存器的值 如果不为0则转至标号处执行程序 如果为0则继续向下执行
如
assume cs:code
code segment
mov ax,2
mov cx,11
s:add ax,ax
loop s
mov ax,4c00h
int 21h
code ends
end
C++这里的cx就是存放着循环次数 s就表示循环标号 我的理解是loop就类似for循环
loop和标号之间的代码可以被叫做循环体
可以再做一个例子 计算123x236
我们可以将123循环加236次
assume cs:code
code segment
mov ax,0
mov cx,236
s:add ax,123
loop s
mov ax,4c00h
int 21h
code ends
end
C++assume cs:code
code segment
mov ax,0fffh
mov ds,ax
mov bx,6
mov al,[bx]
mov ah,0
mov dx,0
mov cx,3
s:add dx,ax
loop s
mov ax,4c00h
int 21h
code ends
end
C++计算ffff:0000字节单元中的数乘以3,结果存储在dx中
段前缀的使用
我们在debug会遇到写入时候是mov al,[0]但是我们用debug载入程序会是mov al,0
并不是把一个内存单元的值赋值给al而是变成了常量0 这时候我们就需要将[idata]前显式地写上段寄存器
mov ax,2000h
mov ds,ax
mov bx,0
mov al,ds:[bx]
C++所以以后我们在遇到写入内存单元的值时候,如果idata是常量,则需要显式地标明段寄存器 ds cs ss es在汇编语言中都称为段前缀
示例:
访问连续的内存单元---loop和[bx]联手
计算ffff:0~ffff:b字节单元中的数据的和,结果存储在dx中
字节的表示范围是0~255 16位寄存器的范围是65535 可以在dx中存放
不可以将内存字节单元累加到dx 因为dx是十六位 所以如果要用add 则我们需要取出内存字单元 而我们需要的是内存字节单元 dl也不行 累加十二个8位数据到一个8位寄存器 会导致进位丢失
可以这样做
mov al,ds:[addr]
mov ah,0
add dx,ax
C++这样我们既可以存放下数据 同时也不会因为字单元数据 累加到错误的数据
我们继续完成上面那个示例
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov bx,0
mov dx,0
mov cx,12
s:mov al,ds:[bx]
mov ah,0
mov dx,ax
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
C++可以再做一个书上的例子 将ffff:0~ffff:b的数据拷贝到0:200~0:20b
可以用ds作为寄存器 先拷贝ffff:[bx]的值 然后重新给ds赋值 将存放ffff:[bx]的dl寄存器赋值给0:[bx]数据
我们这里之说使用附加寄存器 拷贝两个内存字节单元的值
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov ax,0020h
mov es,ax
s:mov dl,[bx]
mov es:[bx],dl
inc bx
loop s
mov ax,4c00h
int 21h
code ens
end
C++在代码段中使用数据
我们如果要使用数据 我们之前会把他写道一个地址 然后去使用它 但是有时候在系统中有些地址并不是我们可以写的 造成的后果也可能很严重 因此我们需要在我们段中定义好数据然后使用 段是系统给我们分配的 所以不必担心会出现问题
assume cs:code
code segment
dw 0123h,0456h,0abch,0defh,0fedh,0cbah,0987h
mov bx,0
mov ax,0
mov cx,8
s:add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end
C++这样的代码也是有问题的 我们载入debug会发现cs代码段把我们定义的数据也变成了代码
cs:ip 指向的是cs:[0] 很显然这不是我们期望的结果
assume cs:code
code segment
dw 0123h,0456h,0abch,0defh,0fedh,0cbah,0987h
start:mov bx,0
mov ax,0
mov cx,8
s:add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start
C++这样在程序加载后 cs:ip将会指向第一条指令在start处 start相当于C语言中的main函数
在代码段中使用栈
问题:利用栈将程序中定义的数据逆序存放
我们可以把上面那个代码修改一下
assume cs:code
code segment
dw 0123h,0456h,0abch,0defh,0fedh,0cbah,0987h
...
mov ax,4c00h
int 21h
code ends
end start
C++从cs:0 到 cs:f 共八个字单元 然后依次入栈再出栈就实现了逆排序 那我们如何给栈中的数据分配空间呢 答案是直接定义空数据 作为栈的空间
即
assume cs:code
code segment
dw 0123h,0456h,0abch,0defh,0fedh,0cbah,0987h
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
...
mov ax,4c00h
int 21h
code ends
end start
C++
然后我们可以把代码段cs通过其他寄存器赋值给ss然后修改栈顶指针寄存器到30 然后再执行我们的入栈操作
可以看到通过栈已经实现了 将内存数据逆序的需要
不同的数据代码栈放在不同的段
我们之前把栈中的数据和栈代码也放在了代码段,虽然这样做也是可以的,但是会显得我们的代码很乱 而且管理起来也不方便 说不定还会出现错误
assume cs:code,ds:data,ss:stack
data segment
dw 0123h,0456h,0abch,0defh,0fedh,0cbah,0987h
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,20h
mov ax,data
mov ds,ax
mov bx,0
mov cx,7
s:push [bx]
add bx,2
loop s
mov bx,0
mov cx,7
s0:pop [bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
code ends
end start
C++这里需要注意的是我们并不需要初始化cs寄存器 因为至少有一个段寄存器存在就是cs寄存器 不指定段前缀则是数据段ds