语言类的学习最好的办法就是放到在功能代码中理解,文字分析特殊情况。
体验软件编程下硬件工作(二)
◆在DOS的DEBUG下,可以通过指令跟踪和修改各个寄存器以及内存中的数据,具体的指令如下:
R查看和改变寄存器内容
D查看内存中内容
E改写内存中的内容
U将内存中的机器指令翻译成汇编指令
T执行一条机器指令
A以汇编指令的格式在内存中写入一条机器指令
这些指令在DEBUG中多用就会熟悉。
◆第一个汇编程序:
- assume cs:codesg
- codesg segment
- start: mov ax,0123h
- mov bx,0456h
- add ax,bx
- add ax,ax
- mov ax,4c00h
- int21h
- codesg ends
- end
⑵XXX segment
......
XXX ends----成对使用的伪指令,定义一个段,分别表示段开始和段结束。
⑶end----伪指令,汇编语言的结束标志。
以上这三条是通过伪指令实现的汇编结构,它们只对编译器有作用。
mov指令:传送指令,将第二个数据送到第一个位置处。
add指令:加法指令,将第一个位置和第二个位置的数据相加,并存到第一个位置处。
mov ax,4c00h
int 21h:程序返回的指令。我们的程序最先以湖边指令的形式存在源程序中,经编译、连接后编程机器码,存储在可执行的文件中,那么怎么得到运行呢?
一个程序A在可执行的文件中,则必须有一个正在运行的程序B,将A从可执行文件中加载在内存后,将CPU的控制权交给A,A才能得到运行。A开始运行后,B暂停运行。而当A运行完毕后,应该将CPU的控制权交还给使它得以运行的程序B,B继续运行。
用我们的系统解释就是,我们的这个可执行程序被commond运行并加载到内存中,系统将CPU的控制权交给我们的程序,等我们的程序执行完后,我们要将CPU的控制权还给commond,所以以上的两条指令就是将控制权返回的指令。
下面进行源程序的编译和连接:
想要进行编译连接工作,我们需要一个编译器,目前汇编语言的编译器有很多,推荐两种,masm和masm32
一个是DOS下的编译器,一个是可视化的编译器(使用方法网上很多,安装好了别忘了设置环境变量,否则会报错)
下图是编译连接的过程过程源文件转换成的机器码:
整个过程可以通过debug *.exe来跟踪。
在DOS系统中EXE文件中的程序加载过程:
◆第二段代码
- assume cs:code,ds:data,ss:stack
- data segment
- dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
- data ends
- stack segment
- dw 0,0,0,0,0,0,0,0
- stack ends
- code segment
- start:mov ax,stack
- mov ss,ax
- mov sp,16
- mov ax,data
- mov ds,ax
- mov bx,0
- mov cx,8
- s:push [bx]
- add bx,2
- loop s
- mov bx,0
- mov cx,8
- s0:pop [bx]
- add bx,2
- loop s0
- mov ax,4c00h
- int 21h
- code ends
- end start
我们逐行来分析代码:
第1行:可能有人会问,是不是通过assume cs:code,ds:data,ss:stack就可以让CPU的cs指向code段,ds指向data段,ss指向stack段了?如果你这样想的话就大错特错了,assume只是一种伪指令,它只能作用于编译器,并不能作用于CPU。这样做的意义不需要知道,只要知道在程序前一定要将各个段和各个段寄存器对应assume就好。
第2~4行:数据段,在这里储存数据。
第5~7行:栈段,这里定义的多少个0是为了说明定义多大的栈空间。
第8~26行:代码段,实现功能代码。
mov ax,stack----stack代表的是栈段的段地址,通过
mov ax,stack
mov ss,ax
mov sp,16 ---- 将栈指针指向栈底。
mov ax,data
mov ds,ax
mov bx,0 ---- 定位数据段的位置。
mov cx,8 ---- cx中存放的是loop(循环)的次数
s:push [bx] ---- s:是标记,在使用跳转命令时这种标记十分有用。
push是将其后数据推向栈顶的意思,与其相反的是pop,将栈顶的数据弹出
loop s ---- 条件跳转,只要cx中不为0就跳转。
◆第三个程序(灵活的寻址方式)
定义一个数据段:
datasg segment
db 'BaSic'
db 'iNfOrMaTiOn'
datasg ends
要将datasg中的第一个字符串转化为大写,第二个字符串转化为小写。
程序如下:
- datasg segment
- db 'BaSiC'
- db 'iNfOrMaTiOn'
- datasg ends
- codesg segment
- start: mov ax,datasg
- mov ds,ax
- mov bx,0
- mov cx,5
- s:mov al,[bx]
- and al,11011111b
- mov[bx],al
- inc bx
- loop s
- mov bx,5
- mov cx,11
- s0:mov al,[bx]
- or al,00100000b
- mov [bx],al
- inc bx
- loop s0
- mov ax,4c00h
- int 21h
- codesg ends
- end start
or al,00100000b ---- 这句是将大写转换成小写
inc bx ----加1的意思
在高级语言中,遇到'BaSiC'这种字符串我们经常会将它存储在数组中,以便进行相关的操作,那么作为一种底层的语言,让我们看看“数组”是怎么实现的:
将datasg segment
db 'BaSic'
db 'iNfOrMaTiOn'
datasg ends中的第一个字符传转化为大写字母,第二个字符串转换成小写字母
实现程序如下:
- assume cs:codesg,ds:datasg
- datasg segment
- db 'BaSiC'
- db 'iNfOrMaTiOn'
- datasg ends
- codesg segment
- start: mov ax,datasg
- mov ds,ax
- mov bx,0
- mov cx,5
- s:mov al,[bx]
- and al,11011111b
- mov[bx],al
- mov al,<span style="color:#ff0000;">[5+bx] </span>;可以写成5[bx]
- or al,00100000b
- mov [bx],al
- mov <span style="color:#ff0000;">[5+bx]</span>,al
- inc bx
- loop s
- mov ax,4c00h
- int 21h
- codesg ends
- end start
在8086CPU中还有两种寄存器,它们的作用和bx很相似,它们就是si和di(它们不能拆成两个8字节寄存器来用)
我们可以利用它们使用[bx+si]、[bx+di]、[bx+si+idata]和[bx+di+idata]的结构,实现高级语言的多重循环。
代码如下:
- assume cs:codesg,ds:datasg,ss:stacksg
- datasg segment
- db 'ibm '
- db 'ibm '
- db 'ibm '
- db 'ibm '
- datasg ends
- stacksg segment
- dw 0,0,0,0,0,0,0,0
- stacksg ends
- codesg segment
- start:mov ax,stacksg
- mov ss,ax
- mov sp,16
- mov ax,datasg
- mov bx,0
- mov cx,4
- s0:push cx
- mov si,0
- mov cx,3
- s:mov al,[bx+si]
- and al,11011111b
- mov [bx+si],al
- inc si
- loop s
- add bx,16
- pop cx
- loop s0
- mov ax,4c00h
- inc 21h
- codesg ends
- end start
在上述的代码中用到了push cx,在汇编编程中可以将需要暂时存储的数据存放到栈中,这和高级语言中函数中的局部变量是一个道理。
◆指令要处理的数据长度
⑴通过寄存器来指明要处理的数据的长度。
例如:
mov ax,1----字长度
mov al,1----字节长度
⑵在没有寄存器名存在的情况下,用操作符X ptr指明内存单元的长度,X在汇编指令中可以是word或者byte
例如:
mov word ptr ds:[0],1 -----访问的内存单元是一个字单元
mov byte ptr ds:[0],1 -----访问的内存单元是一个字节单元
◆div指令(除法指令)
⑴除数:有8位和16位两种,在一个寄存器或内存单元中
⑵被除数:默认的放在AX或DX和AX中,如果除数是8位,除数则是16位,默认在AX中存放;如果除数为16位,则被除数位32位,在DX和AX中存放,DX存放高16位,AX存放底16位
⑶结果:如果除数为8位,则AL存储触发操作的商,AL存储除法操作的余数;如果除数是16位,则AX存储除法操作的商,DX中存储除法操作的余数。
◆几个伪指令:
⑴db定义字节型数据,dw定义字型数据,dd定义双字型数据
⑵dup表示重复的数据
例如:db 3 dup(0)表示 db 0,0,0