《汇编语言》——王爽第三版笔记(4-6章)

第四章:第一个程序

书籍电子版 提取码: b62a

一个源程序从写出到执行的过程

  1. 编写汇编源程序

  2. 对源程序进行编译连接

    编译产生目标文件,windows下生成.obj文件

    连接生成可执行文件,windows下生成.exe文件

  3. 执行可执行文件中的程序

源程序

1. assume cs:codesg ;假设某一段寄存器和程序中的某一个用segment……ends定义的段相关联

1. codesg segment ;定义一个段,段名为codesg,这个段从此开始
	
2.	mov ax, 0123
	mov bx, 0456
	add ax, bx
	add ax, ax
	
	mov ax, 4c00
	int 21

1. codesg ends ;名称为codesg的段到此结束

1. end ;结束编译
  1. 伪代码
  2. 程序
  3. 标号:codesg这个标号指代了一个地址,这个段的名称最终会被编译、连接程序处理为一人段的段地址
结构
  1. 定义一段,名为abc
abc segment
……
abc ends
  1. 在这个段中写入汇编指令
abc segment

	mov ax, 2
	add ax, ax
	
abc ends
  1. 指出程序何时结束
abc segment

	mov ax, 2
	add ax, ax
	
abc ends

end
  1. 与寄存器相关联
assume cs:abc

abc segment

	mov ax, 2
	add ax, ax
	
abc ends

end
  1. 程序要返回
assume cs:abc

abc segment

	mov ax, 2
	add ax, ax
	mov ax, 4c00
	int 21
	
abc ends

end

windows下在DOSbox中使用masm软件

编译

源文件后缀应该是.asm。

masm 1(,asm)

生成.obj文件

连接

link 1

简化方式

masm 1;
link 1;
这样就不用总是点回车了

执行

1

Debug

debug 1.exe

在这里插入图片描述

DOS系统中.exe文件的加载过程:

  1. 程序加载后,ds中存放着程序所在内存区的段地址,这个内存区的偏移地址为0,程序所有内存区的地址就为ds:0
  2. 这个内存区的前256个字节也就是16x16字节,放的是PSP,DOS用来和程序进行通信。这之后就是程序。

DS就是PSP的段地址SA,程序的段地址就是SA:0 + 256 = SA + 10 : 0

(段地址大小最后是要乘16的,所以16*16 = 256)

实验3:编程、编译、连接、跟踪

第五章:[bx]和loop指令

  • [bx]和内存单元的描述

[bx]和[0]类似,段地址在ds中,偏移地址在[]中,

要完整的描述一个内存单元要两个信息

  1. 内存单元的地址
  2. 内存单元的长度
  • 定义()

()表示一个寄存器或者一个内存单元中的内容

(ax)就是ax寄存器中的内容

()中的内容可以是三种类型

  1. 寄存器名 (ax)
  2. 段寄存器名 (ds)
  3. 内存的物理地址 ((ds)*16 + (bx))
  • idata表示常量
mov ax, [1]

可以用idata来统一表示[]中的数字

[BX]

在这里插入图片描述

这其中,[bx]就是表示以ds为段地址,以bx为偏移地址的内存单元。

loop

此指令就是循环

s:  add ax, ax
	loop s ;cx = cx - 1

循环多少次是要有一个条件,这里用cx中的内容,如果cx为0就终止循环,而且每执行到loop s此条语句时cx = cx - 1

显然,如果cx = 1, 那就执行一次add

​ 如果cx = 2,那就执行二次add

​ 如果cx = n, 那就执行n次add

例题:用加法计算123*236

有两种加法方式:

  1. 将123加236次
  2. 将236加123次

显然第二种更快。

assume cs:code

code segment
	mov ax, 0
	mov cx, 123
s:  add ax, 236
	loop s
	
	mov ax, 4c00h
	int 21h

code ends

end

例题:计算ffff:0006单元中的数乘以3,结果放在dx中

这里要考虑的问题有以下几个:

  1. 能不能存的下
  2. 谁来存,怎么用循环
  3. ffff:0006是一个字节型数据,如果直接mov到dx中的话肯定会加上ffff:0007怎么才能让高8位不赋过去?或者说将高8位清0
assume cs:code

code segment
	mov ax, 0ffffh ;在汇编源程序中,数据不能以字母开头
	mov ds, ax
	mov bx, 6
	
	mov al, [bx] 
	mov ah, 0 ;不能保证ah就一定不是0
	
	mov dx, 0
	
	mov cx, 3
	
s:  add dx, ax ;将ax中的值给dx
	loop s
	
	mov ax, 4c00h
	int 21h

code ends

end

Debug命令

g IP CS:IP前的程序被执行

下一条语句是loop时,可以用p命令结束循环

Debug和汇编编译器masm对指令的不同处理

mov ax, [1]

Debug将它解释为[idata],idata是内存单元的偏移地址

masm将它解释为idata,就是一个普通的数字

  • 如果要在编译器中实现Debug中的功能,可以用普通寄存器做一个过渡。
mov ax, 2000h
mov ds, ax
mov bx, 0
mov al, [bx] ;将ds:bx单元中的数据送入al中
  • 也可以用在[idata]前面加上段地址:
mov ax, 2000h
mov ds, ax
mov al, ds:[0] 

这两段代码实现同样的功能

loop和[bx]的联合使用

例题:计算ffff:0~ffff:b单元中数据的和,结果存在dx中

字节数据放到通用寄存器中,记得高位清0

用一个通用寄存器bx来增长

assum cs:code
code segment
	
	mov ax, 0ffffh
	mov ds, ax
	
	mov bx, 0
	mov cx, 12
	
	mov dx, 0
	
s:  mov cl, [bx] ;mov al, [bx]
	mov ch, 0 ;mov ah, 0
	
    mov dx, cx ;mov dx, ax
	inc bx
	loop s
	
	mov ax, 4c00h
	int 21h
	
code ends
end

段前缀

mov ax, ds:[bx]
mov ax, cs:[bx]

这里cs,ds就是段前缀

一段安全的空间

我们面临一种选择,是在操作系统中安全、规矩的编程,还是自由、直接的用汇语言去操作真实的的硬件,了解那此早已被层层系统软件掩盖的真相?在大部分的情况下,我们选择后者。

段前缀的使用

例题:将内存ffff:0~ffff:b单元中的数据复制到0:200~020b单元中
assume cs:code

code segment
	
	mov bx, 0
	mov cx, 12
	
s:  mov ax, 0ffffh
	mov ds, ax
	mov dl, [bx]
	
	mov ax, 0020h
	mov ds, ax
	mov [bx], dl ;送入到的是一个字节空间,而不是一个字空间,所以就不用将dh清0
	
	inc bx
	loop s
	
	mov ax, 4c00h
	int 21h
	
code edns
end

每次设置ds太麻烦了

assume cs:code

code segment
	
	mov ax, 0ffffh
	mov ds, ax
	
	mov ax, 0020h
	mov es, ax	
	
	mov bx, 0
	mov cx, 12
	
s:  mov dl, [bx]
	mov es:[bx], dl
	
	inc bx
	loop s
	
	mov ax, 4c00h
	int 21h
	
code edns
end

实验4:[bx]和loop的使用

第六章:包含多个段的程序

前面的程序只有一个代码段,如果想要其它空间来存放程序,就需要其它空间来存,如果自己来分配的话,可能导致空间不安全,让操作系统来分配就不会出现这样的问题。

  • 程序取得空间的两种方式
  1. 在加载程序的时候为程序分配
  2. 程序在执行过程中向系统申请

原先已经说过,程序头的段地址在cs中,那么其它的内容(不是程序中的内容就应该在其它的段中),这样就有了多个段。

在代码段中使用数据

assume cs:code

code segment

    dw 0123h, 0456h, 0789h, 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

这里说明一下,start和end start是改变ip地址的,cs指向的是程序的头,但是在这段代码中cs:0与cs:1上存放的是2301,dw所定义的数据总共就占了10h(16)个字节。所以mov bx, 0的位置应该是cs:10。ip就应该是从10开始执行,写程序时可以人为设定start后面开始代码的地址就是ip。

end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。用end指令指明了程序入口在start处。

程序的框架:

assume cs:code

code segment
		*
		数据
		*
start:
		*
		代码
		*
code ends
end start

在代码段中使用栈

assume cs:code

code segment

    dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h

    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

start:  mov ax, cs 
        mov ss, ax	;将程序的段入口处设为栈段,也就是上面数据开始,0123h
        mov sp, 30h ;将栈指针sp偏移30h

        mov bx, 0
        mov cx, 8 ;一次传一个字节,总共就要传8次
   s:   push cs:[bx] ;元素入栈
        add bx, 2
        loop s

        mov bx, 0
        mov cx, 8
  s0:   pop cs:[bx] ;元素出栈
        add bx, 2
        loop s0

        mov ax, 4c00h
        int 21h

code ends

end start

检测点6.1

将数据、代码、栈放入不同的段

如果将数据、代码都放入同一个段,那么就会显得混乱。

可以用一个段来存放一种数据。

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 ;ax在栈段
        mov ss, ax ;给栈段
        mov sp, 20h ;16个字型数据,32个字节

        mov ax, data 
        mov ds, ax ;把数据段给ds

        mov bx, 0
        
        mov cx, 8
    s:  push [bx] ;将data中的数据入栈
        add bx, 2
        loop s

        mov bx, 0

        mov cx, 8

    s0: pop [bx] ;将栈入数据送入到data中
        add bx, 2
        loop s0

        mov ax, 4c00h
        int 21h
code ends

end start

以上代码:数据存放在了一个段中,而栈也存放在了一个段中

  • 定义多个段的方法

    与前面定义代码段没有区别,只是自己要想好名字

  • 对段地址的引用

    和原先访问内存地址一样,段问这些段地址中的内容,也要给出段地址和偏移地址。

  • “代码段”、“数据段”、“栈段“完全就是人为的安排

    assume cs:code, ds:data, ss:stack
    

    用这条伪指令,CPU是否就将cs指向code,ds指向data,ss指向stack呢?

    事实并不是这样。

    原先学过,DS是PSP的段地址,这后面256个字节就是PSP中的。也就是段地址加10H后就是cs的段地址。而程序的开头并不是由cs一个人决定的,还有ip也起到作用。

    可以通过改变ip来改变程序的起始位置。

实验5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CCPigSnail

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值