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