寄存器
在DOSBox里面有ax,bx,cx,dx,sp,ip,ds,之类的,如下图
寄存器的种类
可以分为数据类和指令类
AX(AH、AL):累加器
BX(BH、BL):基址寄存器
CX(CH、CL):计数寄存器
DX(DH、DL):数据寄存器 //不过这些都可以修改,一般就数据和指令。
指令
指令的选择是通过cs:ip来选择的。
其中cs代表段地址,ip代表偏移地址
选择该地址内容的数据做为指令。例如:
数据
数据的选择是通过ds:偏移量来确定的,例如:
这里我修改了ds的值,让ds指向1000的地址,在将ax赋值1000:0000地址的值
从这里就可以知道了,数据的选择是ds决定的。
段地址*10H +偏移地址=物理地址
这里可以看到,这两块的地址是一模一样的,类似于:2000+2000和4000的内容是一模一样的,地址含义也一样
我一般认为是绝对地址和相对地址。就比如说这个,1*10H+0=10H,所以绝对地址是0000:0010,而0001就是段地址,0001:0000就是相对地址,有什么问题可以发出来。
call指令
call指令的作用,就是跳转,但是和jmp的区别是会将下一条指令的ip存到栈中
后面使用ret指令回来(将ip修改回来)
这里是运行了call指令,原先ip=100,后面栈中存放下一条指令也就是103。
这里用了ret,将ip修改回来了(通过栈)
栈
-
汇编里面的栈是通过ss:sp来指向的,用这里面的内存来存储数据
栈是先进后出,每次加进来的都要在标记的前面。
在这里面的内存形式0000:0000------->ffff:ffff ,所以每次入栈(push)sp=sp-2,而每次出栈就是(pop)sp=sp+2
栈的最大内存是sp=0时,因为sp=0-2=fffh,所以sp=0时栈内存最大
栈的内存表示,sp的起始到sp被我修改的值,其实是人为的认为,最常见认为sp的起始为0,sp的终止是10h,所以栈内存为10H
具体格式:push ax //是将ax里面的值存入ss:sp内存中
pop bx //是将栈里面的内容存入到bx寄存器中
类似的。
psp区
开始的256个字节是psp区,其作用是为了程序与系统进行通信的
ds是取数据的,ds:0的数据并不是我写的程序的数据,一直到ds:0100才是,所以开始的256个字节是系统与程序进行通信的
一个标准的asm(汇编)文件格式
assume cs:code
code segment
mov ax,2000H ;在汇编语言中,;代表注释,mov代表赋值,add代表相加,pop出栈,push入栈,sub相减,jmp跳转,call
mov ss,ax ;,号右边给左边,这个,号类似于等号
mov sp,0 ;等等
add sp,10H
pop ax
pop bx
push ax
push bx ;交换ax,bx数据
pop ax
pop bx
;inc为增加的英文,inc bx,就是将bx加1,为了节约内存
mov ax,4C00H ;程序返回
int 21H
code ends
end
loop循环以及用jmp实现类型loop
loop比jmp多了一个次数操作,用cx来保存次数
jmp可以实现跳转
-p 可以直接执行完loop指令
-g 类似于go 后面要加地址
loop也可以实现,但是loop需要注意的是cx=1,和cx=0的情况
cx=0时,会出现溢出。
当cx=1时,可以看出并没有循环,原因是走了一遍就算循环了一次,当cx=0时跳出循环,如果还没有走cx已经为0了,cx-1=FFFF,就要循环FFFF次。
用汇编指令写234*145
assume cs:code
code segment
mov ax,0
mov cx,145
addnumber: add ax,234 ;jmp也是和loop一个格式,除开cx次数
loop addnumber
mov ax,4C00H
int 21H
code ends
end
debug的缺陷:有英文字母开头时,前面要加0
比如:mov ax,0ffffH
不加H时表示十进制。
通过es和ds数据寄存器来交换数据实操
assume cs:code
code segment
mov ax,0fffH
mov ds,ax
mov ax,2000H
mov es,ax
mov cx,10H
mov bx,0
adderten: mov al,ds:[bx]
mov es:[bx],al ;es:[bx]是一个字节数据类型,不是字型数据类型
inc bx
loop adderten
mov ax,4C00H
int 21H
code ends
end
通过栈来复制两个地方的数据
assume cs:code
code segment
mov bx,0
mov cx,10H
adderten: mov ax,0fffH
mov ds,ax
mov ax,0
mov al,ds:[bx]
push ax
mov ax,2000H
mov ds,ax
pop ds:[bx]
inc bx
loop adderten
mov ax,4C00H
int 21H
code ends
end
dw 操作数据
assume cs:code
code segment
dw 1,2,3,4,5,6,7,8,9
start:
mov ax,0
mov bx,0
addax: add ax,cs:[bx] ;start 指定操作开始的位置
add bx,2 ;不用start在开头用jmp指令也行
loop addax
mov ax,4C00H
int 21H
code ends
end start
dw和栈联用
assume cs:code
code segment
dw 1122H,2233H,3344H,4455H,5566H,6677H,7788H,8899H,9900H
dw 0,0,0,0,0,0,0,0
dw 0,0,0,0,0,0,0,0
start:
mov ax,cs
mov ss,ax
mov sp,32H ;栈内存是看字节形数据,所以应该是32H
mov bx,0
mov cx,9
addax: push cs:[bx]
add bx,2
loop addax
mov ax,4C00H
int 21H
code ends
end start
数据段,指令段,stack段
这样写可能会出一个bug,就是设置sp的大小就一定要和栈段一致,不然就会执行不知道什么的指令,那个时候就是严重型bug
assume cs:code,ds:data,ss:stack
data segment
dw 1122H,2233H,3344H,4455H,5566H,6677H,7788H,8899H,9900H
data ends
stack segment
dw 0,0,0,0,0,0,0,0
dw 0,0,0,0,0,0,0,0
stack ends
code segment
start:
mov ax,stack
mov ss,ax ;设置栈
mov sp,32 ;如果这样写了,设置栈的大小就一定要和栈段里面的要一致
mov ax,data
mov ds,ax ;设置数据段
mov bx,0 ;这样做就会将ds寄存器当作数据,ss:sp数据当作栈。
mov cx,9
addax: push data:[bx]
add bx,2
loop addax
mov ax,4C00H
int 21H
code ends
end start
段的位置
第一段一般是数据,第二段一般是栈,第三段则是指令
第一和第二段可以换位置,但是第三段不能换,涉及到数据覆盖问题
数据段位置:
栈段位置:
指令段:
段的空间大小
就是16的倍数,如果比如9个字节长度,大小就是16个字节,如果说是23个字节长度,大小就是32个字节。
向大号取整。
比如说这个例子:就是数据是9个字型性长度,所以用了32个字节型长度。
段的练习1:将两个数据段的数据相加,放到另外一个段中
assume cs:code
a segment
db 1,2,3,4,5,6,7,8
a ends
b segment
db 1,2,3,4,5,6,7,8 ;因为数据段寄存器不够了,所以先用栈保存一下位置,然后直接修改es寄存器将es寄存器改成c段那边,然 ;后赋值,后面pop掉es,就行了
b ends
c segment
db 0,0,0,0,0,0,0,0
c ends
code segment
start:
mov ax,a
mov ds,ax
mov cx,8
mov ax,b
mov es,ax
mov bx,0
mov ax,0
addax: push es
mov ax,ds:[bx]
add ax,es:[bx]
mov dx,c
mov es,dx
mov es:[bx],ax
inc bx
pop es
loop addax
mov ax,4C00H
int 21H
code ends
end start
段的练习2:将a段中的前8个字型数据,逆序复制到b段中
assume cs:code
a segment
dW 1,2,3,4,5,6,7,8,0AH,0BH,0CH,0DH,0FH,0FFH
a ends
b segment
dW 0,0,0,0,0,0,0,0
b ends
code segment
start: mov ax,b
mov ss,ax
mov sp,16
mov ax,a
mov ds,ax
mov bx,0
mov cx,8
adder: push ds:[bx]
add bx,2
loop adder
mov ax,4C00H
int 21H
code ends
end start
bx,si,di都为偏移地址寄存器
assume cs:code
a segment
dW 1,2,3,4,5,6,7,8,0AH,0BH,0CH,0DH,0FH,0FFH
a ends
b segment
dW 0,0,0,0,0,0,0,0
b ends
code segment
start: mov ax,a
mov ds,ax
mov si,1
mov di,2
mov bx,0
mov ax,ds:[bx+si] ;唯一的缺点就是不能用减法
add ax,ds:[di]
code ends
end start
and,or运算符
assume cs:code
a segment
dW 1,2,3,4,5,6,7,8,0AH,0BH,0CH,0DH,0FH,0FFH
a ends
b segment
dW 0,0,0,0,0,0,0,0
b ends
code segment
start: mov ax,00001111B
and ax,11110000B
code ends
end start
assume cs:code
a segment
dW 1,2,3,4,5,6,7,8,0AH,0BH,0CH,0DH,0FH,0FFH
a ends
b segment
dW 0,0,0,0,0,0,0,0
b ends
code segment
start: mov ax,00001111B
or ax,11110000B
mov ax,'a'
mov bx,'b'
code ends
end start
字符
同个字母的大写字母比小写字母要小32
将数据段nihao中的i变成大写I
assume cs:code
a segment
db 'nihao,I am you '
a ends
b segment
dW 0,0,0,0,0,0,0,0
b ends
code segment
start: mov ax,a
mov ds,ax
mov bx,1
mov al,ds:[bx]
and al,11011111B ;注意不能在数据段进行and or操作
mov ds:[bx],al
mov ax,4C00H
int 21H
code ends
end start
将数据段中的所有数据变成大写
assume cs:code
a segment
db 'nihao,I am you '
a ends
b segment
dW 0,0,0,0,0,0,0,0
b ends
code segment
start: mov ax,a
mov ds,ax
mov bx,0
mov cx,14
adder: mov al,ds:[bx]
and al,11011111B
mov ds:[bx],al
inc bx
loop adder
mov ax,4C00H
int 21H
code ends
end start
使用si和di偏移地址寄存器