汇编学习笔记

汇编学习

寄存器

被除数 / 除数 = 商

  • ax:
    • 16位乘法计算(mul)存放低位结果
    • 被除数16(除数8位)位除法计算(div)、结果 AL 存放商,AH 存放余数
    • 被除数32(除数16位)位除法计算(div)、结果 AX 存放商,DX 存放余数
  • bx
  • cx:jcxz 有条件跳转指令,判断标志,为0时,跳转
  • dx:
    • 16位乘法计算(mul)存放高位结果
    • 被除数32(除数16位)位除法计算(div)、结果 AX 存放商,DX 存放余数
  • ss:栈中段寄存器
  • sp:栈中偏移地址
  • ds:数据中段寄存器
  • cs:代码段中段寄存器
  • ip:代码段中偏移地址
  • si: 可做普通寄存器使用,类似 ax
  • di: 可做普通寄存器使用,类似 ax
  • bp
  • es: 和 ds 类似,可以左段寄存器,例:es:[si]

定义数据

  • db:data byte 定义字节数据
  • dw:data word 定义字型数据
  • dd:dword(double word)定义双字数据
  • dup:配合 db、dw、dd使用,用来定义重复数据

例:
bd 3 dup (0) == db 0,0,0

显示区内存地址

B8000H ~ BFFFFH 共 32KB 空间,为80 * 25 彩色字符模式的显示缓冲区

在一行中,一个字符占两个字节的存储空间(一个字),低位字节存储字符的 ASCII 码,高位字节存储字符的属性。

对应行

  • 偏移 000~09F 对应显示器上的第一行(80 个字符占 160 字节)
  • 偏移 0A0~13F 对应显示器上的第二行
  • 偏移 140~1DF 对应显示器上的第三行

对应列

  • 00~01 单元对应显示器上的第一列
  • 02~03 单元对应显示器上的第二列
  • 04~05 单元对应显示器上的第三列

属性字节的格式

76543210
BLRGBIRGB
闪烁背景背景背景高亮前景前景前景

例:

  1. 红底闪烁绿字:11000010B
  2. 红底高亮绿字:01001010B
  3. 黑底白字:00000111B

类型 ptr

dword = 2 word = 4 byte

类型包括:

  • byte:字节型
  • word:字型
  • dword:双字型

offset

  • 由编译器处理
  • 功能:取得标号的偏移地址
assume cs:codesg
codesg segment
  
  start: mov ax,offset start ; 相当于 mov ax,0
  s:     mov ax,offset s     ; 相当于 mov ax,3

codesg ends
end start

add 和 adc

  • add 指令执行后会将进位的结果舍去
  • adc 是带进位加法指令,它利用了 cf 位商记录的进位值
    • 指令格式:adc 操作对象1,操作对象2
    • 功能:操作对象 1 = 操作对象 1 + 操作对象 2 + CF

例:
mov ax,2; mov bx,1; sub bx,ax; adc ax,1,执行后 (ax) = 4,相当于计算 (ax) + 1 + CF = 2 + 1 + 1 = 4

sub 和 sbb

  • sub 减法指令,借位将会被舍去
  • sbb 是带借位减法指令,它利用了 CF 位商记录的借位值
    • 指令格式:sbb 操作对象 1,操作对象 2
    • 功能:操作对象 1 = 操作对象 1 - 操作对象 2 - CF

cmp

cmp 是比较指令,cmp 的功能相当于减法指令,只是不保存结果。cmp 指令执行后将对标志寄存器产生影响

  • 指令格式:cmp 操作对象 1 ,操作对象 2
  • 功能:计算操作对象 1 - 操作对象 2 单并不保存结果,仅仅根据计算结果对标志寄存器进行设置

例:
cmp ax,ax 指令执行后相当于 (ax) - (ax) 的运算,结果为 0,单并不保存在 ax 中。指令执行后:zf = 1, pf = 1, sf = 0, cf = 0, of = 0

cmp ax,bx:

  • zf = 1,说明 (ax) = (bx)
  • zf = 0,说明 (ax) != (bx)
  • cf = 1,说明 (ax) < (bx)
  • cf = 0,说明 (ax) >= (bx)
  • cf = 1 并且 zf = 0,说明 (ax) > (bx)
  • cf = 1 或 zf = 1,说明 (ax) <= (bx)
  • sf = 1 且 of = 0 => (ax) < (bx)
  • sf = 1 且 of = 1 => (ax) > (bx)
  • sf = 0 且 of = 1 => (ax) < (bx)
  • sf = 0 且 of = 0 => (ax) >= (bx)
  • 有符号数进行 cmp ax,bx 运算时,可以通过 sf = 1 判断 ax < bx,sf = 0 判断 ax > bx
  • 无符号数不可只通过 sf 进行判断大小关系

jmp

  • 指令格式:jmp 类型 标号(跳转到标号位置)
    • 类型包括:
      • short:依据位移进行跳转,实现段内短转移。修改IP,8位位移,范围 -128~127(指令中包含转移相差位移);地址在编译时给出
      • near ptr:于short类似,为16位位移,范围 -32786~32767;地址在编译时给出
      • far ptr:依据转移目的地址进行跳转,实现段间跳转。修改CS、IP
    • jmp 16位reg:转移地址在reg中
    • word ptr 内存单元地址(段内转移):从内存单元地址处开始存放着一个字,是转移的目的便宜地址
      • 执行后 ip = (内存单元地址)
    • dword ptr 内存单元地址(段间转移):从内存单元地址处开始存放两个字,高地址是转移的目标段,低地址是转移的目的偏移地址
      • 执行后 cs = (内存单元地址 + 2)
      • 执行后 ip = (内存单元地址)

jcxz

有条件跳转指令,所有有条件跳转均为短指令。在机器码中包含转移的位移 ip 范围:-128~127

  • 指令格式:jcxz 标号(如果(cx) = 0,转移到标号处执行)
  • 当 (cx) != 0,什么也不做,程序向下执行
  • 当 (cx) == 0,跳转到标号处执行

loop

loop 指令我i欸循环指令,所有循环指令都是短转移,在对应的机器码中包含转移的位移。对 ip 修改范围 -128~127

  • (cx) = (cx) - 1
  • if ((cx) != 0) jmp short 标号

ret 和 retf

  • ret 用栈中的数据,修改 IP 的内容,从而实现近转移
    • 相当于: pop ip
  • retf 用栈中的数据,修改 CS 和 IP 的内容,实现远转移
    • 相当于:pop ip, pop cs

call

指令格式:call [转移类型] 标号(将当前的 IP 压入栈后,转移到标号处指令)/ 16 位寄存器

  1. 将当前的 IP 或 CS 和 IP 压入栈中
  2. 转移

转移类型包括:

  • far ptr:段间跳转;相当于:push cs; push ip; jmp far ptr 内存单元地址 / reg
  • word ptr:相当于进行:push ip; jmp word ptr 内存单元地址 / reg
  • dword ptr:相当于进行:push cs; push ip; jmp dword ptr 内存单元地址

mul 和 div

mul

两数的相乘要么都是 8 位要么都是 16 位

  • 8 位:一个默认存放在 AL 中,另一个存放在 8 位 reg 或内存字节单元中
  • 16 位:一个默认存放在 AX 中,另一个存放在 reg 或内存字单元中

结果:

  • 8 位:结果默认存放在 AX 中
  • 16 位:结果高位默认存放在 DX 中,低位存放在 AX 中

格式: mul reg / mul (byte ptr/word ptr) 内存单元

div

  • 被除数16(除数8位)、结果 AL 存放商,AH 存放余数
  • 被除数32(除数16位)、结果 AX 存放商,DX 存放余数

shl 和 shr 指令

shl:左移指令,相当于 X = X * 2

  • 将一个寄存器或内存单元中的数据向左移位
  • 将最后移出的一位写入 CF 中
  • 最低位用 0 补充
  • 如果移动位数大于 1 时,必须将移动位数放在 cl 中

例:mov al,01001000b; shl al,1 执行后 (al)=10010000b,cf=0

例:mov al,01001000b; mov cl,3; shl al,cl 执行后 (al)=10010000b,cf=0

shr 逻辑右移指令,它与 shl 所进行的操作刚好相反;相当于 X = X / 2

  • 将一个寄存器或内存单元中的数据向右移位
  • 将最后移出的一位写入 CF 中
  • 最高位用 0 补充
  • 如果移动位数大于 1 时,必须将移动位数放在 cl 中

push 和 pop、pushf 和 popf

在函数调用时,即使用 cal 命令时,将会使 IP 寄存器的值压入堆栈;在使用 ret 命令后,将会使压入堆栈的地址传送给 IP

  • push 入栈指令,以字为单位
    • push ax 将会使 ax 存放到 ss:[sp] 位置处
    • 入栈后 sp = sp - 2(sp 一直指向栈顶元素的位置)
  • pop 出栈指令,以字为单位
    • pop ax 将会使 ss:[sp] 位置处的值存放到 ax 寄存器中
    • 出栈后 sp = sp + 2
  • pushf 将标志寄存器的值压入栈
  • popf 从栈中弹出数据,送入标志寄存器中

模块化设计

参数传递问题

单参问题

  • 参数可以放入 bx 中
  • 结果可以放入 dx 和 ax 中

批量参数

  • 可将字符串的长度放入 cx 中

寄存器冲突

在子程序中使用了父程序所使用的寄存器,将导致寄存器中的内容重写,为了解决此冲突,在子程序中采用栈的形式保存之程序被调用时时所使用的寄存器,当子程序执行完成,则进行弹栈操作

标志寄存器

作用:

  1. 用来存储相关指令的某些执行结果
  2. 用来为 CPU 执行相关指令提供行为依据
  3. 用来控制 CPU 的相关工作方式

这种寄存器被称为 标志寄存器 ,其中存储信息通常被称为 程序状态字(PSW) 。简称 flag

8086CPU 中 flag 寄存器结构,(1、3、5、12、13、14、15 位在 8086CPU 中没有使用,不具有任何含义,而其他几位都具有特殊含义)

1514131211109876543210
OFDFIFTFSFZFAFPFCF

在 Debug 中的表示

标志ofsfzfpfcfdf
值为 1 的标记OVNGZRPECYDN
值为 0 的标记NVPLNZPONCUP

zf 标志

flag 的第六位 zf 为零标志。记录相关指令执行后,其结果是否为 0 .如果结果为 0 ,那么 zf 为 1;否则 zf 为 0(经实践,当计算结果溢出之后为 0 时,不能使 zf 为 0)

例:
mov ax,1; sub ax,1 ,执行后,结果为 0 ,则 zf 为 1;
mov ax,2; sub ax,1 ,执行后,结果为 1 ,则 zf 为 0

  • 影响 zf 的指令:add、sub、mul、div、inc、or、and
  • 不影响 zf 的指令:mov、push、pop

pf 标志

flag 的第 2 为是 pf,奇偶标志位。它记录相关指令执行后,其结果的所有 bit 位中 1 的个数是否位偶数,如果 1 的位数为偶数,则 pf = 1,否则 pf = 0(0 也为偶数)

例:
mov al,1; add al,10 ,执行后结果为 00001011B,其中有 3 个 1 为奇数,则 pf = 0;
mov al,1; add al,2 ,执行后结果为 00000011B,其中有 2 个 1 为偶数,则 pf = 1

sf 标志

flag 的第 7 位是 sf,符号标志位,它记录相关指令执行后,其结果是否为负,如果为负,sf = 1,否则 sf = 0;

cf 标志

flag 的第 0 位是 cf 进位标志位。在 无符号数 运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值

of 标志

flag 的第 11 位是 of 溢出标志位,记录了 有符号数 运算的结果是否发生了溢出。如果发生溢出,则 of = 1,否则 of = 0

例:
mov al,98; add al, 99,指令执行后,cf = 0,of = 1

if 标志

当 CPU 检测到可屏蔽中断信息时,如果 IF = 1,则 CPU 在执行完当前指令后响应中断,引发中断过程;如果 IF = 0,则不响应可屏蔽中断

df 标志和串传送指令

flag 的第 10 位是 df 方向标志位。在串处理指令中,控制每次操作后 si、di 的增减

  • df = 0 每次操作后 si、di 递增
  • df = 1 每次操作后 si、di 递减

格式:movsb,传送字节

功能:执行 movsb 指令相当于进行下面的操作:

  1. ((es) * 16 + (di)) = ((ds) * 16 + (si))
  2. 如果 df = 0 则:(si) = (si) + 1; (di) = (di) + 1
  3. 如果 df = 1 则:(si) = (si) - 1; (di) = (di) - 1

格式:movsw,传送字

功能:执行 movsb 指令相当于进行下面的操作:

  1. ((es) * 16 + (di)) = ((ds) * 16 + (si))
  2. 如果 df = 0 则:(si) = (si) + 2; (di) = (di) + 2
  3. 如果 df = 1 则:(si) = (si) - 2; (di) = (di) - 2

rep movsb,循环传送字节

对应汇编格式:
s:movsb; loop s

rep movsw,循环传送字(cx 为其传送次数)

对应汇编格式:
s:movsw; loop s

8086 CPU 提供以下两条指令对 df 位进行设置

  1. cld 指令:将标志寄存器的 df 位置设置 0
  2. std 指令:将标志寄存器的 df 位置设置 1

检测比较结果的条件转移指令

根据 cmp 指令的比较结果进行转移的指令分为:

  1. 根据 无符号数 的比较结果进行转移的条件转移指令,检测 zf 和 cf 的值
  2. 根据 有符号数 的比较结果进行转移的条件转移指令,检测 sf、of 和 zf 的值

常用根据无符号数的比较结果进行转移的条件跳转指令

指令含义检测的相关标志位
je等于则转移zf = 1
jne不等于则转移zf = 0
jb低于则转移cf = 1
jnb不低于则转移cf = 0
ja高于则转移cf = 0 且 zf = 0
jna不高于则转移cf = 1 且 zf = 1

e:equla、ne:not equal、b:below、nb:not below、a:above、na:not above

内中断

当 CPU 内有如下情况发生,将产生中断信息

  • 除法错误,比如执行 div 指令产生的除法溢出;中断类型码:0
  • 单步执行;中断类型码:1
  • 执行 into 指令;中断类型码:4
  • 执行 int 指令;该指令的格式:int n,指令中的 n 为字节型立即数,是提供给 CPU 的中断类型码

内存地址:0000:0000 ~ 0000:03FF 的 1024 个单元中存放着中断向量表;中断向量表中的一个表项栈两个字的大小,高位地址存放段地址,低地址存放偏移地址

中断过程

  1. (从中断信息中)取得中断类型码
  2. 标志寄存器的值入栈(因为在中断过程中要改变标志寄存器的值,所以先将其保存在栈中) ==> pushf
  3. 设置标志寄存器的第 8 位 TF 和第 9 位 IF 的值为 0 ==> TF = 0, IF = 0(当 TF = 1 时则又会去执行中断程序,将会产生嵌套)
  4. CS 的内容入栈 ==> push CS
  5. IP 的内容入栈 ==> push IP
  6. 从内存地址为中断类型码 * 4 和中断类型码 * 4 + 2 的两个字单元中读取中断处理程序的入口地址设置 IP 和 CS ==> (IP) = (N * 4 + 2)

iret 指令

指令功能用汇编语法描述为:
pop IP; pop CS; popf

算数运算被编译

计算某段代码的长度可以使用算数表达式进行,编译器将会计算其算数表达式的值,并编译为汇编语句。例如计算 s 段代码的长度
offset send-offset s

代码:

s: mov ax,10
   mov ds,ax
   ; ……
send:nop

指令 mov ax,8-4 将会被编译器处理为指令:mov ax,4

指令 mov ax,(5+3)*5/10 将会被编译器处理为指令:mov ax,4

单步中断

CPU 执行完一条指令后,检测到标志寄存器的 TF 位为 1,则产生单步中断,引发中断过程,单步中断的中断类型码为 1。所引发的中断过程:

  1. 取得中断类型码 1
  2. 标志寄存器入栈,TF、IF 设置为 0
  3. CS、IP 入栈
  4. (IP) = (14), (CS) = (14+2)

在中断程序的入口处,应首先设置 TF = 0,以避免一直中断下去

响应中断的特殊情况

当执行 mov ss,ax; mov sp,0 类似命令时,两条指令会连续执行,中间不会产生中断,这是因为在中断时,将会进行 CS:IP 的入栈操作,导致,ss:ip 指向可能会发生异常。

在编写汇编代码时,应保持连续编写设置 ss 和 sp 的操作 mov ss,ax; mov sp,0

外中断

几乎所有由外设引发的外中断,都是可屏蔽中断

可屏蔽中断

  • F = 1 则 CPU 在执行完当前指令后响应中断
  • F = 0 则 CPU 不可响应可屏蔽中断

如果在中断处理程序中需要处理可屏蔽中断,则可以用指令将 IF 置 1;8086 CPU 提供的设置 IF 的指令:

  1. sti,设置 IF = 1
  2. cli,设置 IF = 0

不可屏蔽中断

当 CPU 检测到不可屏蔽中断信息时,则在执行完当前指令后,立即响应,引发中断过程

对于 8086 CPU,不可屏蔽中断的中断类型码固定为 2,所以中断过程中,不需要取中断类型码。不可屏蔽中断过程:

  1. 标志寄存器入栈,IF = 0,TF = 0
  2. CS、IP 入栈
  3. (IP) = (8), (CS) = (0AH)

PC 机键盘的处理过程

键盘输入
  • 键盘按下产生的扫描码成为 通码 。扫描码将被送到主板上相关接口芯片的寄存器中,该寄存器的端口地址为 60h
  • 键盘松开产生的扫描码成为 断码 。扫描码将被送到主板上相关接口芯片的寄存器中,该寄存器的端口地址为 60h
  • 通码的第 7 位为 0
  • 断码的第 7 位为 1

断码 = 通码 + 80h

例:g 键的通码为 22h,断码为 a2h

键盘的输入到达 60h 端口时,相关的芯片就会向 CPU 发出中断类型码为 9 的可屏蔽中断信息,CPU 检测到该中断信息后,如果 IF = 1,则响应中断,引发终端过程,转去执行 int 9 中断例程

0040:17 单元存储键盘状态字节,该字节记录了控制键和切换键的状态。

记录状态置1的状态
0右 SHIFT按下右 SHIFT
1左 SHIFT按下左 SHIFT
2CTRL按下 CTRL
3ALT按下 ALT
4ScrollLock按下 ScrollLock
5NumLock按下 NumLock
6CapsLock按下 CapLock
7Insert按下 Insert

键盘输入处理过程:

  1. 键盘产生扫描码
  2. 扫描码送入 60h 端口
  3. 引发 9 号中断
  4. CPU 执行 int 9 中断例程处理键盘输入

端口操作

  • CPU 最多可以定位 64KB 个不同的端口,则端口地址的范围为 0 ~ 65535
  • 对端口的读写操作只有两条指令:in、out

例: in al,60h 从 60h 号端口读入一个字节

in 和 out 指令中,只能通过 ax 或 al 来存放从端口中读入的数据或要发送到端口的数据, 访问 8 位端口时用 al,访问 16 位端口时用 ax

对 0~255 以内的端口进行读写时:

  • in al,20h ; 从 20h 端口读入一个字节
  • out 20h,al ; 往 20h 端口写入一个字节

对 256~65535 的端口进行读写时,端口号放在 dx 中:

  • mov dx,3f8h ; 将端口号 3f8h 送入 dx
  • in al,dx ; 从 3f8h 端口读入一个字节
  • out dx,al ; 向 3f8h 端口写入一个字节

附录

输出到屏幕中

assume cs:code,ds:data
stack segment
      dw 16 dup (0)
stack ends

code segment
; 名称:show_str
; 功能:在指定的位置,用指定的颜色,显示一个用 0 结束的字符串
; 参数:(dh)=行号(取值范围 0~24),(dl)=列号(取值范围 0~79),(cl)=颜色,ds:si 指向字符串的首地址
; 返回:无
show_str:   ; 保存数据
            push es
            push bx
            push di
            push cx
            push dx

            mov ax,0B800H
            mov es,ax

            ; 计算行
            mov al,160
            mov ah,0
            mul dh
            mov bx,ax ; bx 存储行
            ; 计算列
            mov al,2
            mov ah,0
            mul dl
            mov di,ax ; di 存储列
            mov dl,cl ; dl 存储颜色
            ; 输出到屏幕
lop:        mov ch,0
            mov cl,ds:[si]
            jcxz ok
            mov byte ptr es:[bx+di],cl
            inc si
            inc di
            mov byte ptr es:[bx+di],dl
            inc di
            jmp short lop

ok:         pop dx
            pop cx
            pop di
            pop bx
            pop es
            ret

start:      mov dh,8
            mov dl,3
            mov cl,2
            mov ax,data
            mov ds,ax
            mov si,0
            ; 设置栈
            mov ax,stack
            mov ss,ax
            mov sp,20H

            call show_str
            mov ax,4c00h
            int 21h
code ends

end start

除法溢出的改进

assume cs:code,ds:data

data segment
      db 'Welcome to masm!',0
data ends

stack segment
      dw 16 dup (0)
stack ends

code segment

; 名称:divdw
; 功能:进行不会溢出的除法运算,被除数为 dword 型,除数为 word 型,结果为 dword 型
; 参数:
; - (ax)=dword 型数据的低 16 位
; - (dx)=dword 型数据的高 16 位
; - (cx)=除数
; 返回
; - (dx)=结果的高 16 位,(ax)=结果的低 16 位
; - (cx)=余数
divdw:      push si
            push di
            mov si,ax ; 存放数据低位
            mov ax,dx
            mov dx,0
            div cx    ; 此时 ax 中保存商,dx 中保存余数
            mov di,dx ; 存放余数

            mov dx,ax
            mov ax,0

            ; 将运算结果存入栈中,高位在下面,低位在上面
            push dx
            push ax

            ; mov ax,rem(di,cx)
            mov dx,di
            mov ax,0
            ; mul dx ; ax 中存方数据底位,dx 中存放数据高位
            add ax,si
            div cx ; ax 存放商,dx 中存放余数
            mov cx,dx
            
            pop si
            pop di
            add ax,si
            add dx,di

            pop di
            pop si
            ret

start:      ; 设置栈
            mov ax,stack
            mov ss,ax
            mov sp,20H
            mov ax,4240H
            mov dx,000FH
            mov cx,0AH

            call divdw
            mov ax,4c00h
            int 21h
code ends

end start

数值变为字符

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

data segment
      db 10 dup (0)
data ends

stack segment
      dw 16 dup (0)
stack ends

code segment

; 名称:dtoc
; 功能:将 word 型数据转变为十进制数的字符串,字符串以 0 结尾符
; 参数:(ax)=word型数据,dl:si=指向字符串的首地址
; 返回:无
dtoc:       push si
            push ax
            push bx
            push cx
            push dx

            ; 保存数据
            mov bx,10
            mov di,0
divid:      div bx
            mov cx,dx  ; cx 记录是否跳转
            mov dx,0   ; 将 dx 设置为 0
            jcxz hand
            push cx    ; 将余数 push 到栈中
            inc di     ; 记录入栈次数
            jmp divid
hand:       mov cx,di
write:      pop ax
            add ax,30H
            mov byte ptr ds:[si],al
            add si,1
            loop write

            pop dx
            pop cx
            pop bx
            pop ax
            pop si
            ret
            
start:      mov ax,stack
            mov ss,ax
            mov sp,20h
            mov ax,12666
            mov bx,data
            mov ds,bx
            mov si,0

            call dtoc
            
            mov ax,4c00h
            int 21h
code ends

end start

将字符串中包含 a~z 的字符转化为大写

assume cs:code,ds:data

data segment
  db "Beginner's All-purpose Symbolic Instruction Code.",0
data ends

code segment
  ; 名称 letterc
  ; 功能:将以 0 借位的字符串中的小写字母转变称大写字母
  ; 参数:ds:si 指向字符串首地址
letterc:  mov ch,0
          mov cl,ds:[si]
          jcxz next     ; 当 cx 为 0 时,即 ds:[si] 处的值为 9,跳转到next
          mov bx,61H    ; bx 记录小写字母最小
          mov di,7AH    ; di 记录小写字母最大
          cmp cl,bl
          inc si
          jb letterc
          cmp cx,di
          ja letterc
          dec si
          and cl,11011111B
          mov ds:[si],cl
          inc si
          jmp letterc
   next:  ret

  begin:  mov ax,data
          mov ds,ax
          mov si,0
          call letterc

          mov ax,4c00h
          int 21h
code ends
end begin

重写 0 号中断的处理程序

assume cs:code

code segment
  start:  mov ax,cs
          mov ds,ax
          mov si,offset d0
          mov ax,0
          mov es,ax
          mov di,200h
          mov cx,offset d0end-offset d0
          cld
          rep movsb

          ; 设置向量中断表
          mov ax,0
          mov es,ax
          mov word ptr es:[0*4],200h
          mov word ptr es:[0*4+2],0

          mov ax,4c00h
          int 21h

     d0:  jmp short d0start
          db 'divide error!'
d0start:  mov ax,cs
          ; 设置字符串位置
          mov ds,ax
          mov si,202h
          ; 设置显示区域
          mov ax,0B800H
          mov es,ax
          mov di,160*12+2*33
          ; 设置循环次数
          mov cx,13
      s:  mov al,ds:[si]
          mov es:[di],al
          add di,2
          inc si
          loop s          

          mov ax,4c00h
          int 21h
  d0end:  nop
     
code ends

end start

自制中断程序,显示字符

assume cs:code,ds:data

data segment
  db 'Welcome to masm!',0
data ends

code segment

install:  push ax
          push cx
          push es
          push di
          push ds
          push si

          mov ax,cs
          mov ds,ax
          mov si,offset display
          mov ax,0
          mov es,ax
          mov di,200h
          mov cx,offset displaye-offset display
          cld
          rep movsb
          ; 设置中断向量表
          mov ax,0
          mov es,ax
          mov word ptr es:[7ch*4],200h
          mov word ptr es:[7ch*4+2],0

          pop si
          pop ds
          pop di
          pop es
          pop cx
          pop ax

          ret

  start:  mov dh,10
          mov dl,10
          mov cl,2
          mov ax,data
          mov ds,ax
          mov si,0
          ; 安装程序
          call install

          int 7ch

          mov ax,4c00h
          int 21h

; 功能显示一个用 0 结束的字符串
; 参数 (dh) = 行号,(dl) = 列号,(cl) = 颜色,ds:si 指向字符串首地址
; 返回:无
display:  push es
          push ax
          push cx
          push dx
          push bp
          push si

          mov ch,0
          mov ax,0B800H
          mov es,ax
          mov al,160
          mul dh
          mov di,ax ; di 中存储了行字节数
          mov al,2
          mul dl
          mov bp,ax ; bp 中存储了列字节数
          mov bl,cl ; bl 中存储颜色

      s:  mov ch,0
          mov cl,ds:[si]
          jcxz se
          mov al,ds:[si]
          mov byte ptr es:[bp+di],al
          inc bp
          mov byte ptr es:[bp+di],bl
          inc bp
          inc si
          loop s

     se:  pop si
          pop bp
          pop dx
          pop cx
          pop ax
          pop es

          mov ax,4c00h
          int 21h

displaye: nop

code ends

end start

自定义中断实现 loop 指令

assume cs:code,ds:data

data segment
  db 'Welcome to masm!',0
data ends

code segment

install:  push ax
          push cx
          push es
          push di
          push ds
          push si

          mov ax,cs
          mov ds,ax
          mov si,offset lop
          mov ax,0
          mov es,ax
          mov di,200h
          mov cx,offset lope-offset lop
          cld
          rep movsb
          ; 设置中断向量表
          mov ax,0
          mov es,ax
          mov word ptr es:[7ch*4],200h
          mov word ptr es:[7ch*4+2],0

          pop si
          pop ds
          pop di
          pop es
          pop cx
          pop ax

          ret

  start:  mov ax,0b800h
          mov es,ax
          mov di,160*12
          
          mov bx,offset se-offset s
          mov cx,80

          ; 安装程序
          call install

      s:  mov byte ptr es:[di],'!'
          add di,2

          int 7ch
      se: nop

          mov ax,4c00h
          int 21h

    ; 功能:完成 loop 指令的功能
    ; 参数 (cx) = 循环次数,(bx) = 位移
    ; 返回:无
    lop:  dec cx
          jcxz ok
          pop si ; 保存偏移地址
          sub si,bx
          push si
          iret

     ok:  iret

    lope: nop

code ends

end start
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值