710 likes | 851 Views
……. 第四章 汇编语言程序设计基础. 基本程序结构: 顺序结构 循环结构 分支结构 子程序结构 复合结构:多种程序结构的组合. P116. 循环的初始状态. 初始化. 循 环 体. 循环的工作部分及修改部分. 修改部分. 计数控制 特征值控制 地址边界控制. Y. 循环控制 条件. N. 结. 束. 4.1 循环程序设计. P117. 1 2 3
E N D
…… 第四章 汇编语言程序设计基础 基本程序结构: 顺序结构 循环结构 分支结构 子程序结构 复合结构:多种程序结构的组合 P116
循环的初始状态 初始化 循 环 体 循环的工作部分及修改部分 修改部分 计数控制 特征值控制 地址边界控制 Y 循环控制 条件 N 结 束 4.1 循环程序设计 P117
1 2 3 4 例1. 把BX中的二进制数以十六进制的形式显示在屏幕上。 BX
mov ch, 4 rotate: mov cl, 4 rol bx, cl mov al, bl and al, 0fh add al, 30h ;’0’-’9’ ASCII 30H-39H cmp al, 3ah jl printit add al, 7h ;’A’-’F’ ASCII 41H-46H printit: mov dl, al mov ah, 2 int 21h dec ch jnz rotate
例2. 将正数n插入一个已整序的字数组的正确位置。 • 算法: 将数组中数逐个与N比较,Si为指针 • 若N<Ki,则Ki下移一个单元 • 若NKi,则插在Ki的下一个单元,并结束 • 临界条件: • 若NKn,则插入Kn的下一个单元 • 若N<K1,则K1~Kn后移一个单元, N插在第一个单元 • 循环控制: • 计数控制 元素个数=((字末地址-字首地址) / 2) +1 字数 = (字节末地址-字节首地址) +1 字节数 • 地址边界控制 结束地址为ARRAY_HEAD • 特征值控制: 表示结束条件的值
x -1 3 5 49 15 52 23 37 105 99 78 65 32 n • 例2.将正数n插入一个已整序的字数组的正确位置。 • x dw ? • array_head dw 3,5,15,23,37,49,52,65,78,99 • array_end dw 105 • n dw 32 • mov ax, n • mov array_head-2, 0ffffh • mov si, 0 • compare: • cmp array_end[si], ax • jle insert • mov bx, array_end[si] • mov array_end[si+2], bx • sub si, 2 • jmp short compare • insert: • mov array_end[si+2], ax
例3将首地址为A的字数组从小到大排序(气泡算法,多重循环)例3将首地址为A的字数组从小到大排序(气泡算法,多重循环) • A dw 32,85,16,15, 8 P119
mov cx, 10 • dec cx • loop1: mov di, cx • mov bx, 0 • loop2: • mov ax, A[bx] • cmp ax, A[bx+2] • jle continue • xchg ax, A[bx+2] • mov A[bx], ax • continue: • add bx, 2 • loop loop2 • mov cx, di • loop loop1
判定条件 判定条件 4.2 分支程序设计 P121 A B A B … X (1)两路分支结构 (2)多路分支结构
case 1 case 2 case n case 1 case 2 case 3 ? ? (1)逻辑尺控制 (2)条件控制 (3)地址跳跃表 TABLE DW ONE, TWO, THREE JMP TABLE[SI] … 1000 ONE: … 2000 TWO: … 3000 THREE: … 。。。。。。
例1.设有数组x(x1, … , x10)和y(y1, … , y10),编程计算 z1=x1 + y1 z2=x2 + y2 z3=x3 -y3 z4=x4 -y4 z5=x5 -y5 z6=x6 +y6 z7=x7 -y7 z8=x8 -y8 z9=x9 + y9 z10=x10 + y10 逻辑尺:0 0 1 1 0 1 1 1 0 0 1 减法 0 加法
x dw x1,x2,x3,x4,x5,x6,x7,x8,x9,x10 y dw y1,y2,y3,y4,y5,y6,y7,y8,y9,y10 z dw z1,z2,z3,z4,z5,z6,z7,z8,z9,z10 logic_rule dw 00dch …… mov bx, 0 mov cx, 10 mov dx, logic_rule next: mov ax, x[bx] shr dx, 1 jc subtract add ax, y[bx] jmp short result ; 向前引用 subtract: sub ax, y[bx] result: mov z[bx], ax add bx, 2 loop next
例2.根据AL寄存器中哪一位为1(从低位到高位)把程序例2.根据AL寄存器中哪一位为1(从低位到高位)把程序 转移到8个不同的程序分支去 。(寄存器间接寻址) • cmp al, 0 • je continue • lea bx, branch_table • L: shr al, 1 ;逻辑右移 • jnc add1 • jmp word ptr [bx] • ;段内间接转移 • add1: • add bx, 2 • jmp L • continue: …… • routine1: …… • routine2: …… • …… branch_table dw routine1 dw routine2 dw routine3 dw routine4 dw routine5 dw routine6 dw routine7 dw routine8
4.3 子程序的设计方法 • 过程(子程序)定义伪操作 • procedure_name PROC NEAR ( FAR) • …… • procedure_name ENDP • (1)NEAR属性:调用程序和子程序在同一代码段中 • (段内调用) • (2)FAR属性:调用程序和子程序不在同一代码段中 • (段间调用) P123
保存与恢复寄存器 • subt proc far • push ax • push bx • push cx • push dx • ...... • pop dx • pop cx • pop bx • pop ax • ret • subt endp
(SP)→ (IP) (SP)→ (IP) (CS) 子程序的调用和返回 • 子程序调用(中断调用):隐含使用堆栈保存返回地址 • call near ptr subp • (1) 保存返回地址 • (2) 转子程序 • (IP) ← subp的偏移地址 • call far ptr subp • (1) 保存返回地址 • (2) 转子程序 • (CS) ← subp的段地址 • (IP) ← subp的偏移地址
(SP)→ (IP) (CS) (FLAGS) int 21H INT n(n : 中断类型号) (1) 保存现场和返回地址(FLAGS,CS,IP入栈) (2) 转中断处理程序 (IP) ← (n*4) (CS) ← (n*4+2) 子程序返回: (1)ret (返回地址出栈) (2)iret(IP,CS,FLAGS出栈)
子程序的参数传送 (1)通过寄存器传送参数 (2)通过存储区传送参数 *子程序和调用程序在同一程序模块中,则子程序可 直接访问模块中的变量。 *子程序和调用程序不在同一程序模块中,则有两种 传送方式:建立公共数据区和使用外部符号。 (3)通过地址表传送参数地址 (4)通过堆栈传送参数或参数地址 P124 P125 P127
例3. 十进制到十六进制的转换程序(通过寄存器传送变量) • Decihex segment ; 1016 • assume cs: decihex • main proc far • push ds • sub ax, ax • push ax • repeat: call decibin ; 102 • call crlf • call binihex ; 216 • call crlf • jmp repeat • ret • main endp
Decibin proc near ; 102 • mov bx, 0 • newchar: mov ah, 1 • int 21h • sub al, 30h • jl exit ; <0退出 • cmp al, 9d • jg exit ; >9退出 • cbw • xchg ax, bx • mov cx, 10d • mul cx • xchg ax, bx • add bx, ax • jmp newchar • exit: ret • decibin endp
binihex proc near ; 216 mov ch, 4 rotate: mov cl, 4 rol bx, cl mov al, bl and al, 0fh add al, 30h cmp al, 3ah jl printit add al, 7h ; ‘A’~’F’ printit: mov dl, al mov ah, 2 int 21h dec ch jnz rotate ret binihex endp
crlf proc near mov dl, 0dh mov ah, 2 int 21h mov dl, 0ah mov ah, 2 int 21h ret crlf endp decihex ends end main
例4. 累加数组中的元素(直接访问变量) • data segment • ary dw 1,2,3,4,5,6,7,8,9,10 • count dw 10 • sum dw ? • data ends • code segment • main proc far • assume cs:code, ds:data • start: • push ds • sub ax, ax • push ax • mov ax, data • mov ds, ax • call near ptr proadd • ret • main endp
proadd proc near • push ax • push cx • push si • lea si, ary • mov cx, count • xor ax, ax • next: add ax, [si] • add si, 2 • loop next • mov sum, ax • pop si • pop cx • pop ax • ret • proadd endp • code ends • end start
如果数据段定义如下: data segment ary dw 1,2,3,4,5,6,7,8,9,10 count dw 10 sum dw ? ary1 dw 10,20,30,40,50,60,70,80,90,100 count1 dw 10 sum1 dw ? data ends *如果直接访问内存变量,那么累加数组ary和数组ary1中的元素不能用同一个子程序proadd
例5 . 累加数组中的元素(通过地址表传送变量地址) • data segment • ary dw 10,20,30,40,50,60,70,80,90,100 • count dw 10 • sum dw ? • table dw 3 dup (?) ; 地址表 • data ends • code segment • main proc far • assume cs:code, ds:data • start: push ds • sub ax, ax • push ax • mov ax, data • mov ds, ax • mov table, offset ary • mov table+2, offset count • mov table+4, offset sum • mov bx, offset table • call proadd • ret • main endp
proadd proc near • push ax • push cx • push si • push di • mov si, [bx] • mov di, [bx+2] • mov cx, [di] • mov di, [bx+4] • xor ax, ax • next: add ax, [si] • add si, 2 • loop next • mov [di], ax • pop di • pop si • pop cx • pop ax • ret • proadd endp • code ends • end start ary 10 0000 60 30 40 80 50 90 100 0014 0016 70 20 0002 count 10 0014 sum ? 0016 table 0000 0018 (bx) (si) (di)
例6 . 累加数组中的元素(通过堆栈传送变量地址) • data segment • ary dw 10,20,30,40,50,60,70,80,90,100 • count dw 10 • sum dw ? • data ends • stack segment • dw 100 dup (?) • tos label word • stack ends
code1 segment • main proc far • assume cs:code1, ds:data, ss:stack • start: • mov ax, stack • mov ss, ax • mov sp, offset tos • push ds • sub ax, ax • push ax • mov ax, data • mov ds, ax • mov bx, offset ary • push bx • mov bx, offset count • push bx • mov bx, offset sum • push bx • call far ptr proadd • ret • main endp • code1 ends
(si) (cx) (ax) (ip) (cs) 0 (ds) (sp) (di) (bp) (bp) (bp)+6 0016 (bp)+8 0014 (bp)+0a 0000 • xor ax, ax • next: add ax, [si] • add si, 2 • loop next • mov [di], ax • pop di • pop si • pop cx • pop ax • pop bp • ret 6 • proadd endp • code2 ends • end start code2 segment assume cs: code2 proadd proc far push bp mov bp, sp push ax push cx push si push di mov si, [bp+0ah] mov di, [bp+8] mov cx, [di] mov di, [bp+6]
例8 十六进制到十进制的转换程序(通过寄存器传送变量) • hexidec segment; 1610 • main proc far • assume cs: hexidec • start: • push ds • sub ax, ax • push ax • repeat: • call hexibin ; 162 • call crlf • call binidec ; 210 • call crlf • jmp repeat • ret • main endp
hexibin proc near ; 162 • mov bx, 0 • newchar: • mov ah, 1 • int 21h • sub al, 30h • jl exit • cmp al, 10d • jl add_to • sub al, 27h ; ‘ a ’ ~ ‘ f ’ • cmp al, 0ah • jl exit • cmp al, 10h • jge exit • add_to: • mov cl, 4 • shl bx, cl • mov ah, 0 • add bx, ax • jmp newchar • exit: ret • hexibin endp
binidec proc near ; 210 mov cx, 10000d call dec_div mov cx, 1000d call dec_div mov cx, 100d call dec_div mov cx, 10d call dec_div mov cx, 1d call dec_div ret binidec endp dec_div proc near mov ax, bx mov dx, 0 div cx mov bx, dx mov dl, al add dl, 30h mov ah, 2 int 21h ret dec_div endp
crlf proc near mov dl, 0ah mov ah, 2 int 21h mov dl, 0dh mov ah, 2 int 21h ret crlf endp hexidec ends end start
4.4 I/O程序设计 P135 1. I/O设备的数据传送方式 程序控制方式(查询方式) CPU利用指令反复查询I/O的状态,直到I/O准备好,才进行输入/输出。此间CPU处于循环等待状态,未做有效的工作。 中断方式 CPU在执行程序的同时兼顾对I/O设备的控制或数据传送。当I/O设备准备好(空闲),由I/O通知CPU暂停正在运行的程序,转而处理I/O设备的请求,处理完即返回主程序继续执行。 DMA方式(直接存储器存取方式/成组传送方式) 主要适用于高速I/O设备(如磁盘),CPU向I/O接口提供控制信息(如数据块的首地址及字节数),I/O设备直接和存储器进行成批数据的快速传送。
2. 程序直接控制I/O方式 • I/O端口 • 外设接口由一组寄存器(数据寄存器/状态寄存器/命令寄存器)组成,每个寄存器有一个端口地址(端口号),IBM-PC机的I/O地址空间可达64K(0000~FFFFH)。 • I/O指令 • 主机与外设进行通信的最基本途径。DOS功能调用和BIOS例行程序中的输入/输出功能也是由IN和OUT指令完成的。 • 例:测试某状态寄存器(端口号27H)的第2位是否为1 • IN AL, 27H • TEST AL, 00000100B • JNZ ERROR P136
设备控制寄存器 1 0 端口61H 1 / 0 0 2号定时器门控 控制其它外部设备 与门 放大器 例1. Sound程序 mov dx, 6000 ;声音频率 in al, 61h and al, 11111100b trig: xor al, 00000010b out 61h, al mov cx, 1000 ;声音延迟 delay: loop delay dec dx jne trig P137
例2.CPU要从3个设备轮流输入数据,设备1,2,3的状态寄存器例2.CPU要从3个设备轮流输入数据,设备1,2,3的状态寄存器 • 端口号分别用STAT1,STAT2,STAT3表示,第5位是输入 • 准备位。 • INPUT: IN AL, STAT1 • TEST AL, 20H ;0010 0000b • JZ DEV2 • CALL FAR PTR PROC1 • DEV2: IN AL, STAT2 • TEST AL, 20H • JZ DEV3 • CALL FAR PTR PROC2 • DEV3: IN AL, STAT3 • TEST AL, 20H • JZ NO_INPUT • CALL FAR PTR PROC3 • NO_INPUT: • ……
非屏蔽中断请求 8259A 08 IR0 系统定时器 09 可编程中断控制器 IR1 键盘 2 NMI 0A IR2 彩色/图形接口 INTR 0B 中断逻辑 IR3 保留 CPU 0C IR4 串行通讯口 0D IR5 保留 n 4 0 1 0E IR6 软盘 INT n INTO 除法错 单步 0F IR7 打印机 (PIC) 3. 中断程序设计 中断源:引起中断的事件 外中断(硬中断):外设的I/O请求 —— 可屏蔽中断 电源掉电/奇偶错 —— 非屏蔽中断 内中断(软中断):INT指令/CPU错(除法错、溢出) P140
中断操作的5个步骤: (1) 取中断类型号 (2) 计算中断向量地址 (3) 取中断向量,偏移地址送IP,段地址送CS (4) 转入中断处理程序 (5) 中断返回到INT指令的下一条指令 • 例:BIOS中断 INT 4AH (P.136 图4.13) • 4AH4 = 128H • 4AH4+2 = 12AH • (128H) IP • (12AH) CS P143
类型0的(IP) 00000 类型0的(CS) 类型1的(IP) 00004 类型1的(CS) 类型N的(IP) 4*N 类型N的(CS) 类型255的(IP) 003FC 类型255的(CS) 中断向量表 IBM-PC中断系统能处理256种类型的中断,类型号为0~0FFH。中断向量表是各种中断处理程序的地址表。
DOS功能调用(21H)存取中断向量 • 设置中断向量: • AH=25H • AL=中断类型号 • DS:DX=中断向量 • INT 21H • 取中断向量: • AH=35H • AL=中断类型号 • INT 21H • 返回时送ES:BX=中断向量
例1 用DOS功能调用存取中断向量 • MOV AL, N • MOV AH, 35H • INT 21H ; 取原中断向量 • PUSH ES • PUSH BX ; 保存原中断向量 • PUSH DS • MOV AX, SEG INTHAND • MOV DS, AX • MOV DX, OFFSET INTHAND • MOV AL, N • MOV AH, 25H • INT 21H ; 设置新的中断向量 • POP DS • …… • POP DX • POP DS • MOV AL, N • MOV AH, 25H • INT 21H ; 恢复原中断向量 • RET • ; • INTHAND: …… • IRET
7 6 5 4 3 2 1 0 中断屏蔽寄存器21H 打印机 键 盘 定时器 中断程序的编写步骤: 主程序:(1) 设置中断向量 ( DOS调用 AH=25H, AH=35H ) (2) 设置CPU中断允许位 FLAGS中的IF位=1允许中断 (STI 开中断) =0 禁止中断 (CLI 关中断) (3) 设置中断屏蔽位 I/O端口21H 中断屏蔽寄存器 中断屏蔽位=0 允许中断 =1 禁止中断
7 6 5 4 3 2 1 0 EOI 中断命令寄存器20H • 中断处理子程序: • 保存工作寄存器 (PUSH) • 开中断( STI ) • 中断处理功能 • 关中断( CLI ) • 送中断结束命令( EOI ,仅对硬中断) • 恢复工作寄存器( POP) • 中断返回( IRET ) MOV AL, 20H OUT 20H, AL
例2 .编写一个中断处理程序,要求在主程序运行期间,每隔 10秒响铃一次,同时显示‘The bell is ring!’ • dseg segment • count dw 1 • mess db 'The bell is ring!',0ah,0dh,'$' • dseg ends • cseg segment • main proc far • assume cs:cseg, ds:dseg, es:dseg • start: • push ds • mov ax, ax • push ax • mov ax, dseg • mov ds, ax P147
mov al, 1ch mov ah, 35h int 21h; 取原中断向量 push es push bx ; 保存原中断向量 push ds mov dx, offset ring mov ax, seg ring mov ds, ax mov al, 1ch mov ah, 25h int 21h; 设置新的中断向量 pop ds in al, 21h and al, 11111110b out 21h, al; 设置中断屏蔽位,允许定时器中断 sti ; 开中断
mov di, 2000 delay: mov si, 3000 delay1: dec si jnz delay1 dec di jnz delay; 主程序工作(期间每秒产生中断18.2次) pop dx pop ds mov al, 1ch mov ah, 25h int 21h; 恢复原中断向量 ret main endp P148
ring proc far • push ds • push ax • push cx • push dx • mov ax, dseg • mov ds, ax • sti ; 开中断,允许更高级的中断 • dec count • jnz exit • mov dx, offset mess • mov ah, 09 • int 21h; 显示‘The bell is ring!’
mov dx, 100 in al, 61h and al, 11111100b sound: xor al, 2 out 61h, al mov cx, 140h wait1: loop wait1 dec dx jne sound; 响铃 mov count, 182 exit: cli ; 关中断 pop dx pop cx pop ax pop ds iret ring endp cseg ends end start P149