8086 第三部分

8086 第三部分 汇编语言程序设计

1 用汇编语言写的源程序

1.1 汇编的编译过程

伪指令

//这是汇编固定的返回值
mov ax,4c00h
int 21h

1.2 汇编程序的结构

分号表示注释


2 由源程序到程序运行

2.1 编写汇编代码

  1. 编写源文件asm:随便你用什么编辑器(txt都行),反正写好一个asm文件就行
  2. 调用dos环境下的masm编译器对文件进行编译,编译后可能产生下述几个文件:
例如:masm hello
简化后续写法: masm hello;
  • *.obj:目标文件,就是编译后的最终结果
  • *.LST:列表文件,是编译器将源程序编译为目标文件的过程中的中间结果
  • *.CRF:交叉引用文件,同列表文件一样,是比那一过程中的中间结果
  1. 链接 ---- link xx文件名,会生成以下文件
  • *.exe:可执行文件
  • *.MAP:映像文件,连接程序将目标文件连接为可执行过程中产生的中间结果
  • *.LIB:库文件,包含了一些可以调用的子程序,如果我们的程序中调用了某一个库文件的子程序,就需要在连接的时候,将这个库文件和我们的目标文件连接到一起,生成可执行程序。
    有时候会提示你no stack segment这个警告,说明没有栈段,对于简单程序可以不管
  1. 执行可执行程序

3 debug跟踪程序的执行

  1. DS:存程序在内存中的段地址,CS:存代码段的起始地址,那么DS-CS之间有个256字节的PSP(程序段前缀),DOS用来和程序进行通信。
  2. 程序加载进内存后,CX寄存器存的是代码的长度,单位为字节

补充debug使用参数

P:继续执行(proceed),类似T命令,单步执行指令,但是在遇到子程序or中断的时候,直接执行,然后显示结果

G(Go):运行命令,直接运行直到程序结束or遇到断点,也可以后面接个地址,说明从该地址开始run


4 [...]和(...)的约定

idata ----约定表示为一个常量

比如MOV AX,[idata] 可以代表mov ax,[1] mov ax,[2] mov ax,[3]

5 Loop指令

指令格式:Loop 标号

每次循环执行下述操作

  1. (CX)=(CX)-1
  2. 判断CX的值,如果不为0,则跳转到标号处执行程序,如果为0则向下执行

示例代码如下

;loop指令示例程序
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中存循环次数,记得定义标号

在汇编程序中,数据不能以字母开头,比如我要给ax寄存器存ffff地址

要写成mov ax,0ffffh


6 段前缀的使用

6.1 一个“异常”的现象

;考虑如下异常代码
assume cs:code
code segment
    mov ax,2000h
    mov ds,ax
    mov a1,[0]
    mov b1,[1]
    mov c1,[2]
    mov d1,[3]

    mov ax,4c00h
    int 21h
code ends
end

在dos环境中运行debug,-u查看汇编代码,发现原本想让0地址的内容传进ax,变成了传递数字00进ax

解决办法

引入段前缀:在访问内存单元的指令中,显式指明内存单元的段地址,比如DS: CS: SS: ES:

上述问题的对策,在[idata]前显示的写上段寄存器,比如

mov ax,2000h
mov ds,ax
mov bx,0
mov al,ds:[bx]   
mov al,ds:[0]    ;注意看第二个参数写法

7 在代码段中使用数据

因为直接操作内存是非常危险的,所以程序中的数据是存在段上的,运行时由操作系统分配空间。
段有:代码段,数据段,栈段
每个段中都能有数据

;比如在定义在代码段上的数据
dw 0123H,0456H,0789H  ;定义一个字
db               ;定义一个字节
dd               ;定义一个双字

标识程序代码开始的地方

程序的开始


8 在代码段中使用栈

要想在程序中使用栈,要通过定义空数据获得

比如下面一道题:
将程序中定义的数据逆序存放。
思路:开辟一个栈空间,将内存中的数据 入栈后再次出栈 存回去就完成了逆序操作

assume cs:codesg
codesg segment
    dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h  ;存了8个字的数据
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   ;开辟16个0的空间作为栈空间,因为上面是8个字
start:mov ax,cs
    mov ss,ax  ;设置栈基地址,和cs同一个地址
    mov sp,30h ;设置栈顶指针

    ;入栈
    mov bx,0
    mov cx,8   ;8次循环
s:  push cs:[bx]
    add bx,2    ;每次加2表示 每次移动2字节,也就是一个字
    loop s

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

    mov ax,4c00h
    int 21h
codesg ends
end start

当然上述的做法是把数据、代码、栈 放在同一个段中的结果

注意程序默认拥有一个段,所以开始时可以不用给这个寄存器赋值


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

随着项目的增长,我们如果把所有内容都放一个段中,就会容易混乱。 所以我们要分段!!!

同样以上面那题为例

;开头就声明各个段寄存器的关联
assume sc:code,ds:data,ss:stack
;数据段
data segment
    dw 0123H,0456H,0789H,0ABCH,0DEFH,OFEDH,0ABCH,0987H
data ends
;栈段
stack segment
    dw 0,0,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 ax,4c00h
    int 21h
code ends
end start
posted @ 2022-11-15 13:55  wenli7363  阅读(59)  评论(0编辑  收藏  举报