(2011.11.12)汇编_王爽_全书_学习小记
终于,将花了一个月左右的时间,对汇编语言的知识有了一个大概的了解,最后也做了一个小小小项目算是对自己这段时间的学习的一个基础巩固。汇编学习之路 -- 告一段落。
-----------------------------
目录:
(2011.10.26)汇编_王爽_第五章_学习小结 --使用循环及段地址
http://blog.csdn.net/neicole/article/details/6912711
(2011.10.28)汇编_王爽_第六章_学习小结 --定义多个段及程序入口
http://blog.csdn.net/neicole/article/category/909705
(2011.10.29)汇编_王爽_第七章_学习小结 --循环问题及大小写转换和寄存器寻址方式
http://blog.csdn.net/neicole/article/details/6916429
(2011.10.29)汇编_王爽_第08章_学习小结 --寻址方式运用和除法指令dup指令
http://blog.csdn.net/neicole/article/details/6917503
(2011.11.01)汇编_王爽_第09章_学习小结 --转移指令的原理
http://blog.csdn.net/neicole/article/details/6926593
(2011.11.02)汇编_王爽_第10章_学习小结 --CALL和RET指令
http://blog.csdn.net/neicole/article/details/6928937
(2011.11.02)汇编_王爽_第11章_学习小结 --标志寄存器
http://blog.csdn.net/neicole/article/details/6930195
(2011.11.03)汇编_王爽_第12章_学习小结 --内中断
http://blog.csdn.net/neicole/article/details/6933178
(2011.11.04)汇编_王爽_第13章_学习小结 --int指令
http://blog.csdn.net/neicole/article/details/6937837
(2011.11.04)汇编_王爽_第14章_学习小结 --端口
http://blog.csdn.net/neicole/article/details/6938726
(2011.11.07)汇编_王爽_第15章_学习小结 --外中断
http://blog.csdn.net/neicole/article/category/909705
(2011.11.09)汇编_王爽_第16章_学习小结 --直接定址表
http://blog.csdn.net/neicole/article/details/6953839
(2011.11.10)汇编_王爽_第17章_学习小结 --使用BIOS进行键盘输入
http://blog.csdn.net/neicole/article/details/6958389
(2011.10.20)汇编知识:CPU中的寄存器知识整理
(2011.11.21)汇编知识:寻址方式小结
(2011.11. 08)汇编知识:指令系统总结
(2011.11.21)汇编知识:段的综述
-----------------------------
内容:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.10.20)汇编知识:CPU中的寄存器知识整理
-> 通用寄存器
01.AX 累加器:一个通用寄存器,在乘法、除法、输入/输出以及某些调整指令中专用。
02. BX 基址寄存器:用来寻址存储器。
03. CX 计数寄存器:用作计数的存储器,移动和循环用CL计数,重复的串指令和LOOP指令用CX计数。
04. DX 数据寄存器:通用寄存器,用于保存乘法结果的高位,除法的被除数的高位和余数。
05. BP 基指针寄存器:存放内存地址,指向堆栈段中的一个存储单元。
06. DI 目标变址寄存器:寻址串指令的目标数据串。
07. SI 源变址寄存器:串指令寻址源数据段
-> 段寄存器:用来与微处理器中的其他寄存器联合产生存储器地址。
08. CS 代码段寄存器:定义存放代码的存储器的起始地址。
09. DS 数据段寄存器:定义数据段的起始地址,(数据段的起始地址和偏移地址结合寻址数据)
10. ES 附加段寄存器:定义附加段的起始地址。
11. SS 堆栈段寄存器:定义堆栈段的起始地址。
12. FS和GS 附加的段寄存器:对80386、80486和Pentium微处理器有效,让程序访问两个外加存储器段。
附:CS:IP CPU将要执行的代码段
SS:SP 指向栈顶的元素,入栈减,出栈加(数据从高放到低)
DS:[x] 取DS的数据作为内存单元的段地址。
-> 专用寄存器
13. IP 指令指针:寻址内存中代码段的下一条指令。
14. SP 堆栈指针:寻址椎栈的存储区,指向堆栈段下一个要访问的单元。
15. EFLAGS 标志寄存器:指示微处理器的状态并控制它工作。
->附:标志寄存器中的一些指令
16. C 进位:进位标志保留加法以后的进位或减法以后的借位。
17. P 奇偶性:奇校验0,偶校验1.
18. A 辅助进位:保留加法后的结果3位和4位间的进位,或减法后的结果3、4位间的借位。
19. Z 零:表示一个算法或逻辑操作的结果是否为零。
20. S 符号:算术符号,负1,正0
21. T 陷阱:陷阱中断,调试一个程序,以便找到错误和故障。允许1,禁止0.
22. D 方向:自动递减1,自动递增0
23. O 溢出:有符号数做加减法时出现,溢出1,没溢出0
24. IOPL 输入/输出特权级:00级最高特权,11级最低特权级
25. NT 任务嵌套:保户模式下当前执行的任务嵌套于另一任务中。
26. RF 恢复:接受调试故障,接受0,不接受1
27. VM 调试:虚拟8086状态下工作0,保护模式下工作1
28. AC 队列检查:即不是字又不是双字的边界上寻址一个字或双字1.
29. VIF 虚拟中断:虚拟方式下中断标志位的复制。
30. VIP 虚拟中断暂挂:提供Pentium微处理器提供虚拟模式的信息。
31. ID 标识:标识标志指示Pentium微处理器支持CPUID指令。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.21)汇编知识:寻址方式小结
寻址方式 |
名称 |
含义 |
常用格式举例 |
[idata] |
直接寻址 |
EA = idata; SA = (ds) |
[idata] |
[bx] |
寄存器间接寻址 |
EA = (bx); SA = (ds) |
[bx] |
[si] |
EA = (si); SA = (ds) |
||
[di] |
EA = (di); SA = (ds) |
||
[bp] |
EA = (bp); SA = (ss) |
||
[bx+idata] |
寄存器相对寻址 |
EA = (bx) + idata; SA = (ds) |
用于结构体:[bx].idata 用于数组:idata[si],idata[di] 用于二维数组:[bx][idata] |
[si+idata] |
EA = (si) + idata; SA = (ds) |
||
[di+idata] |
EA = (di) + idata; SA = (ds) |
||
[bp+idata] |
EA = (bp) + idata; SA = (ss) |
||
[bx+si] |
基址变址寻址 |
EA = (bx) + (si); SA = (ds) |
用于二维数组 [bx][si] |
[bx+di] |
EA = (bx) + (di); SA = (ds) |
||
[bp+si] |
EA = (bp) + (si); SA = (ss) |
||
[bp+di] |
EA = (bp) + (di); SA = (ss) |
||
[bx+si+idata] |
相对基址变址寻址 |
EA = (bx) + (si) +idata; SA = (ds) |
用于表格(结构)中的数组项: [bx].idata[si] 用于二维数组: Idata[bx][si] |
[bx+di+idata] |
EA = (bx) + (di) +idata; SA = (ds) |
||
[bp+si+idata] |
EA = (bp) + (si) +idata; SA = (ss) |
||
[bp+di+idata] |
EA = (bp) + (di) +idata; SA = (ss) |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11. 08)汇编知识:指令系统总结
名 |
解释 |
指令 |
名 |
解释 |
指令 |
01.数据传送指令
|
-> 这些指令实现寄存器和内存,寄存器和寄存器之间的单个数据传送
|
001. mov 002. push 003. pop 004. pushf 005.popf 006. xchg
|
04. 转移指令
|
-> 可以修改IP,或同时修改CS和IP的指令统称为转移指令
|
030. 无条件转移指令:jmp 031. 条件转移指令: jcxz, je,jb,ja, jnb, jna 032. 循环指令:loop 033. 过程:call, ret,retf 034. 中断:int ,iret
|
02. 算术运算指令
|
-> 这些指令实现寄存器和内存中的数据的算数运算
|
007. add 008. sub 009. adc 010. sbb 011. inc 012. dec 013. cmp 014. imul 015. idiv 016. aaa
|
05. 处理机控制指令
|
-> 对标志寄存器或其他处理机状态进行设置
|
035. cld 036. std 037. cli 038. sti 039. nop 040. clc 041. cmc 042. stc 043. hlt 044.wait 045. esc 046. lock
|
03. 逻辑指令
|
-> 它们的执行结果都影响标志寄存器的相关标志位
|
017. and 018. or 019. not 020. xor 021. test 022. shl 023. shr 024. sal 025. sar 026. rol 027. ror 028. rcl 029. rcr |
06. 串处理指令
|
-> 这些指令对内存中的批量数据进行处理。
|
047. movsb 048. movsw 049. cmps 050. scas 051. lods 052. stos
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.21)汇编知识:段的综述
-> 概念
01.我们可以将一段内存定义成一个段,用一段地址指示段,用偏移地址访问段内的单元。
02.我们可以用一个段存放数据,将它定义为“数据段”
03.我们可以用一个段存放代码,将它定义为“代码段”
04.我们可以用一个段当作栈,将它定义为“栈段”
-> 使用:让CPU按照我们的安排来访问这些段的规则
05.对于数据段,将它的段地址放在DS中,用mov、add、sub等访问内存单元的指令时。
06.对于代码段,将它的段地址放在CS中,将段中第一条指令的偏移地址放在IP中。
07.对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地址放在SP中。
-> 总结
08.可见,不管我们如何安排,CPU将内存中的某段内容当作代码,是因CS:IP指向了那里:CPU将某段内存当作栈,是因为SS:SP指向了那里。我们一定要清楚,什么是我们的安排,以及如何让CPU按我们的安排行事。要非常清楚CPU的工作机理,才能在控制CPU按照我们的安排运行的时候做到游刃有余。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.10.26)汇编_王爽_第五章_学习小结
本章主要内容:
1. 学会如何使用loop指令;
2. 知道如何定义一个段;
3. 知道隐含段地址,显式使用段地址与偏移地址方法。
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;源程序名:502_loop1_循环相加.asm
;实现功能:简单的loop循环指令1
assumecs:code ; code标号->指代地址,assume将有特定用途的段与寄存器关联起来,assume cs
codesegment ;定义一个段名为code的段 segment的作用是定义段
mov ax, 0
mov cx, 236 ;设定cx(计数寄存器)的大小[此处作用是设定循环次数]
s:add ax, 123 ;用标号s标识一个地址,从标号s开始的地址作为循环语句
loops ;执行 loop的循环,会分两步走,第一步是cx递减,第二步是执行s的语句
mov ax, 4c00h ;这两条"汇编指令"所实现的功能就是程序返回程序
int21h ;同上,解释:即将CPU的控制权交还给使它得以运行的程序
codeends ;段结束的"伪指令"
end ;通知编译器程序结束的伪指令
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;源程序名:503_loop2_循环相加.asm
;实现功能:简单的loop循环指令2
assumecs:code
codesegment
mov ax, 0fffh ;在汇编程序中,数据不能以字母开头,所以要在前面加0
mov ds, ax
mov bx, 6 ;以上,设置ds:bx指向ffff:6
mov al, [bx]
mov ah, 0 ;以上,设置(al)=((ds*16)+(bx)),(ah) = 0
mov dx, 0 ;累加寄存器清零
mov cx, 3 ;循环3次
s:add dx, ax
loops ;以上累加计算(ax)*3
mov ax, 4c00h
int21h ;程序返回
codeends
end
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
; 0.程序名称:505_loop和[bx]的综合应用_计算总和存放数据.asm
; 1.题目要求:计算ffff:0~ ffff:b单元中的数据的和,结果存储在dx中。
; 2.题目需注意的问题:
; 01.结果不会超出dx所存储的范围:ffff:0~ ffff:b内存单元中的数据是字节型数据,2的8次方,范围在0~255之间,12个这样的数据相加,结果不会大于65535,可在dx存放。
; 02.ffff:0~ffff:b中的数据不能直接累加到dx中,ffff:0~ffff:b中的数据是8位,不能直接加到16位的寄存器dx中。
; 03.将ffff:0~ffff:b中的8位的数据,累加到16位的寄存器dx中,有两种方法
; 001. (dx) = (dx)+ 内存中的8位数据 [问题:两个运算对象的类型不匹配]
; 002. (dl) = (dl)+ 内存中的8位数据 [问题:结果有可能超界]
; 3.解决方法:
; 用一个16位的寄存器来做中介,将内存单元中的8位数据赋值到一个16位的寄存器ax中,再将ax中的数据加到dx上,从而使两个运算对象的类型匹配并且结果不会超界。
assumecs:code
code segment
mov ax, 0ffffh ;上节内容,因为在汇编中,数据不能以字母开头,所以就要加个0了
mov ds, ax
mov bx, 0 ;初始化ds:bx指向ffff:0
mov dx, 0 ;初始化累加寄存器dx,(dx) = 0
mov cx, 12 ;初始化循环计数寄存器cx,(cx) = 12
s:mov al, [bx]
mov ah, 0
add dx, ax ;间接向dx中加上((ds)*16 + (bx) )单元中的数值
inc bx ; ds:bx指向下一单元 inc的作用是递增1
loops
mov ax, 4c00h
int21h
code ends
end
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:508_段前缀的使用_内存单元中的数据复制1.asm
;程序功能:将内存ffff:0~ffff:b单元中的数据复制到0:200~0:20b单元中。
;程序分析:0:200~0:20b单元等同于0020:0~0020:b单元,它们描述的是同一段内存空间。
; 复制过程可用loop循环实现。
assumecs:code
code segment
mov bx, 0 ;偏移地址由零开始(bx) = 0
mov cx, 12 ;循环12次
s: ;在这个循环中,bl起到一个临时存储的作用,通过改变地址,先存放,再取出
mov ax, 0ffffh
mov ds, ax ; (ds) = 0fffh
mov dl, [bx] ;(dl) = ((ds)*16 + (bx)),将ffff:bx中的数据送入dl
mov ax, 0020h
mov ds, ax ; (ds) = 0020h
mov [bx],dl ; ((ds)*16+(bx))=(dl),将dl的数据送入0020:bx
inc bx ; (bx) = (bx) + 1
loops
mov ax, 4c00h
int21h ;程序返回
code ends
end
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:509_段前缀的使用_内存单元中的数据复制2.asm
;程序功能:将内存ffff:0~ffff:b单元中的数据复制到0:200~0:20b单元中。
;程序分析:0:200~0:20b单元等同于0020:0~0020:b单元,它们描述的是同一段内存空间。
; 复制过程可用loop循环实现。
;附:DOS方式下,一般情况,0:200 ~ 0:2ff这段空间中没有系统或其他程序的数据或代码,我们需要直接向一段内存中写入内容时,就使用0:200~0:2ff这段空间。
assumecs:code
code segment
mov ax, 0ffffh
mov ds, ax
mov ax, 0020h
mov es, ax ; (es) = 0020h
mov bx, 0
mov cx, 12 ;循环12次
s: ;该程序是对上一程序的循环优化程序
mov dl, [bx] ;隐式地使用了ds这一个段地址,现在全地址是ds:[bx]
mov es:[bx], dl; ((es)*16+(bx))=(dl),将dl中的数据送入0020:bx
inc bx
loops
mov ax, 4c00h
int21h
code ends
end
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:实验4_习题2_数据传送.asm
;题目要求:向内存0:200~0:23F依次传送数据 0~63(3FH),程序中只能使用9条指令,9条指令中包括“mov ax, 4c00h”和“int 21h”。
;题目分析:很明显,如果直接按要求去写程序的话,程序指令会超出题目要求,有10条了。
; assume cs:code
; codesegment
;
; mov ax, 0
; mov ds, ax
; mov bx, 200
; mov cx, 3Fh
;
; s:
; mov ds:[bx], al
; inc ax
; inc bx
; loop s
;
; mov ax, 4c00h
; int 21
;
; codeends
; end
;想要减少指令条数,要从ax,dx, bx这些寄存器中下手了
;思路是要使它们的变量变少,也就是说,可以想办法统一变量的数据缩少变量数量
;可以做的是:改变内存的偏移地址,段地址 0020:00~ 0020:3F跟原题的地址是一样的
assumecs:code
codesegment
mov ax, 0020h
mov ds, ax
mov bx, 0
mov cx, 3Fh
s:
mov ds:[bx], bl
inc bl
loop s
mov ax, 4c00h
int 21
codeends
end
;这一段程序,比上一段少了一条循环指令内的内容 inc bx
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.10.28)汇编_王爽_第六章_学习小结
本章主要内容:
1. 学会如何定义程序入口;
2. 知道如何定义多个段;
3. 知道如何运用所定义的多个段。
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:603_start_指明程序入口.asm
;程序功能:将指定区域的数据放入栈并将数据按逆序存放到某区域中
assumecs:codesg
;用dw定义16个字型数据,在程序加载后,将取得16个字的内存空间,存放这16个数据。
;在后面的程序中将这段空间当作栈来使用
codesgsegment
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
; dw即“defineword“,每个dw数据占了16个字节的空间.
start:
mov ax, cs
mov ss, ax
mov sp, 30h ;将设置栈顶ss:sp指向cs:30h[注意:这里是十六进制]
mov bx, 0
mov cx, 8
s: ; 利用栈,可以将程序中定义的数据逆序存放,在这里,先将全部数据入栈
push cs:[bx]
add bx, 2
loops
mov bx, 0
mov cx, 8
s0:
pop cs:[bx]
add bx, 2
loops0 ; 以上依次出栈8个字型数据代码段0~15单元中
mov ax, 4c00h
int21h
codesgends
endstart ;指明程序的入口在start处[即将start:那里的地址设为cs:ip的地址]
; (end后加标号代表入口地址,标号可任意取,此时标号为start)
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:604_定义不同的段.asm
;程序功能: 定义多个段,并将数据按逆序存放到某区域中
; 附: 一个段有8个字节,连续定义的段,连续存储
assumecs:code, ds:data, ss:stack
;开始定义数据段
datasegment
dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah,0987h
dataends
;开始定义栈段
stacksegment
dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
stackends
;开始定义代码段
codesegment
start: ;设置代码开始执行的入口地址
; ss:sp栈段
mov ax, stack ;将stack段的段地址送入ax中
mov ss, ax
mov sp, 20h ;设置栈顶ss:sp指向stack:20
mov ax, data
mov ds, ax ; ds指向data段
mov bx, 0 ; ds:bx指向data段中的第一个单元
mov cx, 8
s:
push [bx]
add bx, 2
loops ;以上将data段中的0~15单元中的8个字型数据依次入栈
mov bx, 0
mov cx, 8
s0:
pop [bx]
add bx, 2
loops0 ;以上依次出栈8个字型数据到data段的0~15单元中
mov ax, 4c00h ;程序返回
int21h
codeends
endstart
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:实验5_习题5_不同段中数据相加.asm
;程序功能:将a段和b段中的数据依次相加,将结果存入到c段中
;程序分析:
assumecs:code, ds:a, ds:b, ds:c
asegment
db 1, 2, 3, 4, 5, 6, 7, 8
aends
bsegment
db 1, 2, 3, 4, 5, 6, 7, 8
bends
csegment
db 0, 0, 0, 0, 0, 0, 0, 0
cends
codesegment
start:
mov bx, 0
mov cx, 8
s:
mov ax, 0 ;将a段的第bx个数据放入al中
mov dx, a
mov es, dx
mov al, es:[bx] ;将b段的第bx个数据与al相加
mov dx, b
mov es, dx
add al, es:[bx]
mov dx, c ;将相加结果放入c段中
mov es, dx
mov es:[bx], al
add bx, 2 ;地址递增,开始循环
loops
mov ax, 4c00h
int 21h
codeends
endstart
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.10.29)汇编_王爽_第七章_学习小结
本章主要内容:
1. and 和 or指令的使用
2. 嵌套循环问题的处理
3. 大小写转换的方法
4. 各种寻址方式的应用。[bx], [bx +idata],[bx +si(或di)], [bx +si +idata]
5. 栈的应用
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:704_大小写转换_[bx]实现.asm
;程序功能:将内存单元中的小写字母转换成大写字母,大写字母转换成小写字母
assumecs:codesg, ds:datasg
datasgsegment
db'BaSiC' ; db占一个字节,每个字母占一个字节,这些字母连续存储在内存单元中
db'iNfOrMaTiOn'
datasgends
codesgsegment
start:
mov ax, datasg
mov ds, ax ;设置ds指向datasg段
mov bx, 0 ;设置(bx) = 0, ds:bx指向'BaSiC'的第一个字母
mov cx, 5 ;设置循环次数为5,因为'BaSiC'有5个字母
;转大写
s:
mov al, [bx] ;将ASCII码从ds:bx所指向的单元中取出
and al, 11011111B ;将al中的ASCII码的第五位置为零,变为大写字母
;使用and指令,能将二进制代码置0
; ASCII码中,大写字母的二进制下标为5的值为0
mov [bx], al ;将转变后的ASCII码写回原单元
inc bx ; (bx)加1,ds:bx指向下一字母
loops
;转小写
mov bx, 5 ;设置(bx) = 5, ds:bx指向'iNfOrMaTiOn'的第一个字母
mov cx, 11 ;设置循环次数为11,因为'iNfOrMaTiOn'有11个字母
s0:
mov al, [bx]
or al, 00100000B ;将al中的ASCII码的第5位置置为1,变为小写字母
;使用or指令,能将二进制代码置1
; ASCII码中,小写字母的二进制下标为5的值为1
mov [bx], al
inc bx
loops0
mov ax 4c00h
int21h
codesgends
endstart
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:706_大小写转换_[bx+data]数组处理.asm
;程序功能:将内存单元中的小写字母转换成大写字母,大写字母转换成小写字母
assumecs:codesg, ds:datasg
datasgsegment
db'BaSiC' ; db占一个字节,每个字母占一个字节,这些字母连续存储在内存单元中
db'iNfOrMaTiOn'
datasgends
codesgsegment
start:
mov ax, datasg
mov ds, ax
mov bx, 0
mov cx, 5
s:
mov al, [bx] ;定义第一个字符串
and al, 11011111b ; and运算,小写转换为大写
mov [bx], al ;这里隐含了[0+bx],这种方法可以在同一个循环中定义多个字符串中的字符
;mov al, 0[bx] ; 上面语句的第二种表达方法
;mov al, [bx].0 ; 上面语句的第三种表达方法
mov al, [5+bx] ;定义第二个字符串
or al, 00100000b ;or运算,大写转换为小写
mov al, [5+bx] ;这样就可以方便地定义数组了
;mov al, 5[bx] ; 上面语句的第二种表达方法
;mov al, [bx].5 ; 上面语句的第三种表达方法
inc bx
loops
mov ax, 4c00h
int21h
codesgends
endstart
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:707_大小写转换_[bx+data]二维数组处理_嵌套循环的使用.asm
;程序功能:将内存单元中的小写字母转换成大写字母,大写字母转换成小写字母
assumecs:codesg, ds:datasg
datasgsegment
db'ibm ' ;长度为16个字节
db'dec '
db'dos '
db'vax '
datasgends
;上面的数据段中,可以将整个数据看作一个二维数组(4行*16列)
; ------------------------------------
; | 0 1 2 3 4 5 6 7 8 9 A B C D E F |(16进制内存单元位置表示)
; |00 i bm |
; |10 d ec |
; |20 d oc |
; |30 v ax |
; ------------------------------------
codesgsegment
start:
mov ax, datasg
mov ds, ax
mov bx, 0 ;该程序中,bx用于控制外循环中的步进大小
mov cx, 4 ;此处为外循环的次数
s0:
mov dx, cx ;将外循环的次数临时保存在dx中
mov si, 0 ;该程序中,si用于控制内循环中的步进大小,每次从外循环进入时,清零
; si和di是8086CPU中和bx功能相近的寄存器
; si目标变址寄存器
; di源变址寄存器
mov cx, 3 ;此处为内循环的次数
s:
mov al, [bx+si] ;此时的偏移地址是 bx+ si
and al, 11011111b ;转为大写,第五位的数据设为零
inc si
loops ;以上为内循环内容
add bx, 16
mov cx, dx ;将dx中存放的外层循环的计数值恢复到cx中
loops0 ;外层循环中的loop指令将cx中的数值减1
mov ax, 4c00h
int21H
codesgends
endstart
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:708_大小写转换_[bx+data]二维数组处理_嵌套循环的使用_栈的使用.asm
;程序功能:将内存单元中的小写字母转换成大写字母,大写字母转换成小写字母
assumecs:codesg, ds:datasg, ss:stacksg
datasgsegment
db'ibm ' ; 长度为16个字节
db'dec '
db'dos '
db'vax '
datasgends
;上面的数据段中,可以将整个数据看作一个二维数组(4行*16列)
; ------------------------------------
; | 0 1 2 3 4 5 6 7 8 9 A B C D E F |(16进制内存单元位置表示)
; |00 i bm |
; |10 d ec |
; |20 d oc |
; |30 v ax |
; ------------------------------------
stacksgsegment ;定义一个段,用来做栈段,容量为16个字节
dw 0, 0, 0, 0, 0, 0, 0, 0
stacksgends
codesgsegment
start:
mov ax, stacksg
mov ss, ax
mov sp, 16
mov ax, datasg
mov ds, ax
mov bx, 0
mov cx, 4
s0:push cx ;将外层循环的cx值压栈
mov si, 0
mov cx, 3 ; cx设置为内层循环的次数
s: mov al, [bx+si]
and al, 11011111b
mov [bx+si], al
inc si
loops
add bx, 16
pop cx ;从栈顶弹出原cx的值,恢复cx
loops0 ;外层循环中的loop指令将cx中的计数值减1
mov ax, 4c00H
int21H
codesgends
endstart
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.10.29)汇编_王爽_第08章_学习小结
本章主要内容:
1. div除法指令
2. dup指令
3. 寻址方式的综合应用
4. 偏移地址的寄存器的使用:bx,si, di,bp
5. ptr的使用
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
一.需注意的知识点:
1. push指令只进行字操作
2. mov word ptr ds:[0], 1 用wordptr指明了指令访问的内存单元是一个字单元。
3. 当在偏移地址中包括bp时, […bp],段地址就默认在ss中,(ss)*16+bp
4.bx,si,di,bp四种寄存器可以单独出现,也可以双个出现,不用多个同时出现,
当两个出现的时候,只能bx与si或di搭配,bp与si或di搭配,即可以将其分为两组:
组1是 (bx,bp),组2是(si, di), 同组内的成员不能同时出现,只能异组搭配。
二. 寻址方式小结
5. 直接寻址 [idata]
6. 寄存器间接寻址 [bx]
7. 寄存器相对寻址 [bx][idata]
8. 基址变址寻址 [bx][si]
9. 相对基址变址寻址 [bx].idata[si]
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:实验7_寻址方式在结构化数据访问中的应用.asm
;程序功能:将几个数据段的内容放到一段数据段中,并使用除法计算数值。
;题目如下:编程,将data段中的数据按如下格式写入到table段中,
; 并计算21年中的人均收入(取整),结果也按下面的格式保存在table段中。
; ____________________________________________________________________________________________
; |行起始地址 |年份(4字节)|空|收入(4字节)|空|雇员数(2字节)|空|人均收入(2字节)|空|
; | table:0 | 1975 |格| 16 |格| 3 |格| ??? |格|
; | table:10H | 1976 | | 22 | | 7 | | ??? | |
; | table:20H | 1977 | | 382 | | 9 | | ??? | |
; | table:30H | 1978 | | 1356 | | 13 | | ??? | |
; | table:40H | 1979 | | 2390 | | 28 | | ??? | |
; | table:50H | 1980 | | 8000 | | 38 | | ??? | |
; ..........................................................................
; ..........................................................................
; | table:140H | 1995 | | 5937000 | | 17800 | | ??? | |
;|____________|______________|__|______________|__|________________|__|__________________|__|
assumecs:codesg, ds:data, ds:table
datasegment
db'1975','1976','1977','1978','1979','1980','1981','1982','1983','1984','1985'
db'1986','1987','1988','1989','1990','1991','1992','1993','1994','1995'
;以上表示21年的21个字符串(年份)(4字节)
dd 16, 22, 382, 1356, 2390, 8000, 16000, 24486,50065, 97479, 140417, 197514, 345980
dd 590827, 803530, 1183000, 1843000, 2759000,3753000, 4649000, 5937000
;以上表示21年公司总收入的21个dword型数据(收入)(4字节)
dw 3, 7, 9, 13, 28, 38, 130, 220, 476, 778, 1001,1442, 2258, 2793, 4037, 5635
dw 8226, 11542, 14430, 15257, 17800
;以上是表示21年公司雇员人数的21个word型数据
dataends
tablesegment
db 21dup ('year summ ne ?? ') ; dup指令,用于数据的重复
; db重复的次数 dup (重复的字节型数据)
; dw重复的次数 dup (重复的字型数据)
; dd重复的次数 dup (重复的双字型数据)
; dd:用来定义dword(double word,双字)
tableends
codesgsegment
start:
mov ax, data
mov ds, ax ;首先将data数据段放入ds中,用于提取数据时所用的地址
mov ax, table
mov es, ax ;然后将table数据段放入es中,用于放入时所用的地址
mov bx, 0 ;将源内存分为两个起点,此为其一
mov si, 168 ;将源内存分为两个起点,此为其二
mov di, 0 ;目的内存的偏移地址
mov cx, 21 ;一共有二十一年,因为设置循环次数为21
s:
mov al,' ' ; byte ptr指明了指令访问的内存单元是一个字节单元
mov es:[di].4, al
mov es:[di].9, al
mov es:[di].12, al
mov es:[di].15,al ;以上,先将一年中数据为空格的内存单元设为空格
mov ax, ds:[bx].0 ;将年份放入目的地址
mov es:[di].0, ax
mov ax, ds:[bx+2].0
mov es:[di+2].0, ax
mov ax, ds:[bx].84 ;将收入放入目的地址
mov es:[di].5, ax
mov ax, ds:[bx+2].84
mov es:[di+2].5,ax
mov ax, ds:[si] ;将雇员数放入目的地址
mov es:[di].10, ax
; div除法指令
; div 8位 16位
; 被除数 ax dx*10000H+ ax
; 除数 [...] [...]
; 商 al ax
; 余数 ah dx
; 计算人均收入:总收入除以人数
mov dx, es:[di].7
mov ax, es:[di].5 ;将被除数放入dx与ax中
div word ptr es:[di].10 ;进行除法并且设定除数
mov es:[di].13,ax ;结果会被放到ax中,将ax的结果放入目标内存中
add di, 16
add si, 2
add bx, 4
loops
mov ax, 4c00h
int21h
codesgends
endstart
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.01)汇编_王爽_第09章_学习小结
本章内容:
1. 转移指令——同时修改CS和IP的指令的统称。
2. 段内转移——只修改IP
——短转移——IP修改范围为 -128至127.
——近转移——IP修改范围为 -32768至 32767
3. 段间转移——同时修改CS和IP
4. offset功能是取得标号的偏移地址。
5. jmp无条件转移指令,可以只修改IP,也可以同时修改CS和IP
6. jmp short 标号[IP] jmp near标号[IP] jmp far标号[CS:IP]jmp 16位的reg[IP]
7. jmp word ptr 内存单元地址(段内转移) jmpdwordptr 内存单元地址(段间转移)
8. jcxz 标号(如果(cx) =0, 转移到标号处执行。)
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:实验9_根据材料编程_在屏幕中间显示不同颜色的字符串.asm
;程序功能:在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串'Welcome to masm'
;程序材料:
;在内存中,有一个 80* 25彩色字符模式显示缓冲区,B8000H - BFFFFH共 32KB的空间。
;需记忆:向这个地址空间写入数据,写入的内容将立即出现在显示器上。
;显示器可以显示25行,每行80个字符,每个字符可有256种属性(背景色,前景色,闪烁,高亮等组合信息)
;一行共有80个字符,占160个字节。
;一个字符在内存中占两个字节,(偶地址)低位存放字符的ASCII码,(奇地址)高位存放字符的属性。
;例:
;在B8000H至B8F9FH为第一屏(25行,9+F=25-1),其中,偏移地址 000 - 09F 对应显示器上的第一行,(9F = 10 * 16 - 1)
;属字节的格式:
; 7 6 5 4 3 2 1 0
; BL R G B I R G B
;当它们的位数为1的时候会被激活:BL(7):闪烁,(654)背景,I(3)高亮,(210)前景色,R(4,2)红色,G(5,1)绿色,B(6,0)绿色.
;注意:
;闪烁的效果必须在全屏DOS方式下才能看到。
;程序分析:
;简单地说,编写该程序的方法就是将字符放到B8000H - BFFFFH的空间就可以了。
;那么,这里可以使用mov指令,还有loop指令。
assumecs:code, ds:data
;开始定义数据段,也就是题目要求的字符
datasegment
db'welcome to masm!'
dataends
;开始定义代码段,并且设定程序执行的入口
codesegment
start:
mov ax, data ;找到字符串的起点
mov ds, ax
mov al, 8 ;设定段地址
mov ah, 12
mov es, ax
mov bx, 31 ;设定字符偏移地址的起点,列
add bx, 384 ;行
mov si, 0 ;一行有80个字符,要显示的有15个字符
mov di, 0 ;要将字符放到中间,先设定中点为40,然后向两间展开
;也就是说,左边是40 - 7 = 33,33 - 1 = 31
;减1原因,由零开始
mov cx, 15 ;循环十五次
;先将 'welcometo masm!'这十五个字符放入到屏幕中间
putcharin:
mov al, ds:[si] ;放入字符
mov es:[bx+di], al
inc di
;开始设置字体颜色,但由于每个字符串的颜色不同,所以这里需要设置几个循环跳点
cmp si, 7
jaenear ptr c7 ; >=7时,跳到C7
c0:
mov al, 01000000B ;绿色
jmpnear ptr endloop
c7:
cmp si, 11
jaenear ptr c11
mov al, 00100100B ;绿底红色
jmpnear ptr endloop
c11:
mov al, 01110001B ;白底蓝色
endloop:
mov es:[bx + di], al
inc si
inc di
loopputcharin
mov ax, 4c00h
int21h
code ends
endstart
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.02)汇编_王爽_第10章_学习小结
本章内容:
1. call 与 ret指令的使用
2.使用call和ret对子程序的编写
3. mul指令(乘法)的使用
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
知识点归纳:
现在,个人根据段内转移和段间转移,在程序中调用方法的对应将其归纳为两类,主要功能是实现程序中子程序的编写,相当于高级语言中的函数调用,知识点如下:
一.段内转移 (仅改IP)
-> 指令:call标号 \ call word ptr[内存单元地址]
原理:实现段内转移原理相当于以下2个步骤
第1步:push IP
第2步:jmp nearptr 标号 \ jmp wordptr [内存地址]
-> 指令:ret
原理:改IP,实现段内转移跳出,执行指令完毕后,让IP指向ret指令后的内存单元
第1步:pop IP
二. 段间转移 (改CS:IP)
->指令:call farptr标号 \ calldword ptr [内存单元地址]
原理:实现段间转移原理相当于以下3个步骤
第1步:push CS
第2步:push IP
第3步:jmp farptr 标号 \ jmpdword ptr [内存单元地址]
-> 指令:retf
原理:实观段间转移跳出原理相当于以下2个步骤
第1步:pop IP
第2步:pop CS
三:具有子程序的源程序的框架
assumecs:code
code segment
main: ;这个程序,两个子程序嵌套了
: ; 一个子程序运行完后,接着会跳到另一个子程序运行
: ; 最后都运行完后,会逐步跳出子程序,回到主程序
call sub1 ;现开始调用子程序
:
:
mov ax, 4c00h
int 21h
sub1: ;子程序sub1开始
:
:
call farptr sub2 ; 将子程序嵌套在sub1中,调用子程序sub2
:
ret ;子程序返回
sub2: ;子程序sub2开始
: ;为了不使寄存器发生冲突,还可以按以下方法编写子程序
: ;子程序中使用的寄存器入栈
: ;子程序内容
: ;子程序中使用的寄存器出栈
retf ;子程序返回
code ends
end main
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1009_模块化程序设计_乘法的使用.asm
;程序功能:计算data段中第一组数据的3次方,结果保存在后面一组dword单元中。
assumecs:code
datasegment
dw 1, 2, 3, 4, 5, 6, 7, 8 ;即求这一行的每一个数的三次方,结果放入下一行中
dd 0, 0, 0, 0, 0, 0, 0, 0
dataends
codesegment
start:
mov ax, data
mov ds, ax
mov si, 0 ; ds:si指向第一组word单元 (原数字)
mov di, 16 ; ds:di指向第二组dword单元 (计算结果)
mov cx, 8
s:
mov bx, [si]
callcube ;调用子程序(功能:计算三次方)
mov [di], ax ;存放计算结果的低位
mov [di].2, dx ;存放计算结果的高位
add si, 2 ; ds:si指向下一个word单元
add di, 4 ; ds:di指向下一个dword单元
loops
mov ax, 4c00h
int21h
cube: ;子程序cube(功能:计算三次方)
mov ax, bx
mul bx
mul bx
ret ;乘法:mul指令 8位(数值小于255) 16位(数值大于等于255)
; 乘数1 al ax
; 乘数2 8位(内存单元)或(reg) 16位(内存单元)或(reg)
; 结果 ax dx:[ax]
codeends
endstart
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1012_模块化程序设计_大小写转换_解决寄存器冲突的问题.asm
;程序功能:将一个全是字母,以零结尾的字符串,转化为大写。
assumecs:code
datasegment
db'word', 0
db'unix', 0
db'wind', 0
db'good', 0
dataends
codesegment
start:
mov ax, data
mov ds, ax
mov bx, 0
mov cx, 4 ;共有四组长度相同的单词
s:
mov si, bx
callcapital ;调用功能函数
add bx, 5 ;每组单词的长度为5
loops
mov ax, 4c00h
int21h
;子程序说明:将一个全是字母,以零结尾的字符串,转化为大写
;子程序参数:ds:si指向字符串的首地址
;子程序结果:没有返回值
capital: ;子程序中使用的寄存器入栈
push cx ;这是为了防止影响主程序
push si
change:
mov cl, [si]
mov ch, 0
jcxzok ; jcxz当cx为零时,跳至标号
;此处巧妙地利用了cl作为数据结束标记
and byte ptr [si], 11011111b
inc si
jmpshort change
ok:
pop si ;寄存器按相应的顺序出栈,数值还原到主程序中。
pop cx
ret ;跳出子程序
codeends
endstart
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:实验10_编写子程序_显示字符串.asm
;程序功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串
;子程序描述:
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串
;参数:(dh) =行号(取值范围0~24),(dl) =列号(取值范围 0~79),
; (cl) =颜色, ds:si指向字符串的首地址
;返回: 无
;应用举例:在屏幕的8行3列,用绿色显示data段中的字符串
assumecs:code
datasegment
db'Welcome to masm!',0
dataends
codesegment
start:
mov dh, 8 ;行号
mov dl, 3 ;列号
mov cl, 2 ;颜色
mov ax, data
mov ds, ax ;将数据段地址放入至ds中
mov si, 0 ;将si设置为数据段的偏移地址
callshow_str
mov ax, 4c00h
int21h
;程序分析:
;此处要实现显示功能,实际上就是要将ds:[si]的数据搬到B8000H - BFFFFH中
;而现在,段地址我将其设为es = B8000,偏移地址[bx = dh].[di = dl],循环时,di递增
show_str:
mov al, dh ;令[bx = dh]
mov ah, 160 ;每行160个字节
mul ah
sub ax, 160
mov bx, ax
mov al, dl ;令[di = dl]
mov ah, 0
mov di, ax
mov ax, 0B800H; 设置目的地址
mov es, ax
s:
mov ch, [si] ;判断si中的数据是否为零
mov cl, 0
jcxzok
mov al, ds:[si]
mov es:[bx+di], al
inc di
mov ah, 2 ;颜色
mov es:[bx+di],ah
inc di
inc si
jmpshort s
ok:
ret
codeends
endstart
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.02)汇编_王爽_第11章_学习小结
本章内容:
1. 介绍标志寄存器flag中的标志,以及各标志相应的指令,如何运用这些标志。
2. pushf和popf,可以直接访问标志寄存器,pushf将flag压栈,popf将flag弹出数据。
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
8086CPU的flag寄存器结构如下所示:
15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
OF DF IF TF SF ZF AF PF CF
溢 方 符 零 奇 进
标志寄存器在Debug中的表示,对照表:
AX = 0000 BX = 0000 CX = 0000 DX = 0000 SP = FFEE BP = 0000 SI = 0000 DI = 0000
DS = **** ES = **** SS = **** IP = 0100 NV UP EI PL NZ NA PO NC
标志 值为1标记 值为0的标记
of OV NV
sf NG PL
zf ZR NZ
pf PE PO
cf CY NC
df DN UP
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1106_adc指令_两个128位数据进行相加.asm
;程序说明:编写一个子程序,对两个128位数据进行相加
;参数: ds:si指向存储第一个数的内存空间,ds:di指向第二个数的内存空间
; 因数据为128位,所以需要8个字单元,也可理解为需要8次ax
; 由低地址单元到高地址单元依次存放128位数据由低到高的各个字。
; 运算结果存储在第一个数的存储单元中。
; adc指令介绍:
; adc是带进位加法的指令,它利用了CF位上记录的进位值
;附:sbb指令是带借位减法指令
;指令格式:adc操作对象1,操作对象2
;指令功能: 操作对象1 =操作对象1 + 操作对象2 +CF
;指令实例: adcax, bx
;实现功能: (ax)= (ax) + (bx) + CF
;指令实例: sbb ax,bx
;实现功能: (ax)= (ax) - (bx) - CF
;主要思想:加法可以分两步来进行:
; 1.低位相加 2.高位相加再加上低位相加产生的进位值
add128:
push ax
push cx
push si
push di
sub ax, ax ;将CF设零
mov cx, 8
s:
mov ax, [si] ;第一位数放入ax中
adc ax, [di] ;将已进入ax的第一位数与第二位数相加
mov [si], ax ;相加结果已放入ax,再将ax移回第一位数的内存单元中保存
inc si ;因为它为字单元,因为加2
inc si ;不使用add,为了防止影响CF,下次执行adc时可能会出错。
inc di
inc di
pop di
pop si
pop cx
pop ax
ret ; 程序返回
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1108_cmp指令_统计某一范围内的数据个数.asm
;程序功能:统计F000:0处32个字节中,大小在(32, 128)的数据的个数。
assumecs:code
codesegment
start:
mov ax, 0f000h
mov ds, ax
mov bx, 0
mov dx, 0
mov cx, 32
s:
mov al, [bx]
cmp al, 32
jbs0 ;低于32则跳出计数
cmp al, 128
jas0 ;高于128则跳出计数
inc dx
s0:
inc bx
loops
mov ax, 4c00h
int21h
codeends
endstart
; cmp是比较指令,cmp的功能相当于减法指令,只是不保存结果。
; cmp指令格式: cmp操作对象1,操作对象2
; cmp功能: 操作数1 -操作数2, 根据计算结果对寄存器进行设置。
;影响flag的相关各位,指令执行后:zf, pf, sf, cf, of会有所改变。
;如果溢出导致了实际结果为负,那么逻辑上真正的结果必然为正。
;如果因为溢出导致了实际结果为正,那么逻辑上真正的结果必然为负。
;无符号数的比较结果进行转移的条件转移指令
;指令 含义 检测相关标志位
; je 等于则转移 zf = 1
; jne 不等于则转移 zf = 0
; jb 低于则转移 cf = 1
; jnb 不低于则转移 cf = 0
; ja 高于则转移 cf = 0且 zf = 0
; jna 不高于则转移 cf = 1或 zf = 1
; e:表示 equal ||ne :表示 not equal
; b:表示 below ||nb :表示 not below
; a:表示 above ||na :表示 not above
;不管转移指令前面是什么指令,只要CPU执行je指令时,zf = 1,那么就会发生转移。
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1110_DF标志和串传送指令_复制字符串.asm
;程序功能:编程,用串传送指令,将data段中的第一个字符串复制到它后面的空间中。
assumecs:code, ds:data
datasegment
db'Welcome to masm!'
db 16dup (0)
dataends
codesegment
mov ax, data
mov ds, ax
mov si, 0 ; ds:si指向data:0
mov es, ax ;
mov di, 16 ; es:di指向data:0010
mov cx, 16 ; (cx) = 16, rep循环16次
cld ;设置df=0,正向传送
repmovsb
mov ax, 4c00h
int21h
codeends
end
; cld指令:令 DF = 0,每次操作后si, di递增
; std指令:令 DF = 1,每次操作后si, di递减
;指令:movsb /mov sw
;功能:
; 第一步: moves:[di], byte/word ptr ds:[si] ;8086并不支持这样的指令,这里只是个描述
; 第二步: df = 0时, inc si, inc di
; df = 1时, dec si, decdi
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:实验11_将字符串的小写字母转换为大写字母.asm
assumecs:codesg
datasgsegment
db"Beginner'sAll-purpose Symbolic Instruction Code.", 0
datasgends
codesgsegment
begin:
mov ax, datasg
mov ds, ax
mov si, 0
callletterc
mov ax, 4c00h
int21h
; 名称:letterc
; 功能:将以0结尾的字符串中的小写字母转变成大写字母
; 参数:ds:si指向字符串的首地址
letterc:
c0:
mov cl, ds:[si]
mov ch, 0
jcxzok
c1:
mov al,'a'
mov ah, ds:[si]
cmp ah, al
jbloops ; ah小于al时,直接跳出,设定'a'~'z'的范围
c2:
mov al,'z'
cmp ah, al
jaloops ; ab大于al时,直接跳出,设定'a'~'z'的范围
change:
and ah,1101111b ;将字符转换为大写
mov ds:[si], ah
loops:
inc si
jmpc0
ok:
ret
codesgends
endbegin
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.03)汇编_王爽_第12章_学习小结
本章内容:
1. 了解什么是内中断。
2.如何处理内中断。
3.iret指令与中断处理程序的写法。
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
一.概念
1. 内中断:
CPU可以在执行完当前正在执行的指令之后,检测到从CPU外部发送过来的或内部产生的一种特殊信息(中断信息),并且可以立即对所接收到的信息进行处理。
2. 中断源
中断信息的事件,即中断信息的来源。
3. 中断向量表
中断处理程序入口地址的列表。
(在内存中保存着256个中断源所对应的中断处理程序入口0000:0000~0000:03FF)
4. 中断过程
CPU收到中断信息后,要对中断信息进行处理,首先将引发中断过程。
二.产生中断原因:
1. 除法错误,比如,执行div指令产生的除法溢出(中断内型码0)
2. 单步执行(中断内型码1)
3. 执行into指令(中断内型码4)
4. 执行int 指令
二.中断过程主要任务
用中断码在中断向量表中找到中断程序的入口地址,设置CS和IP。
1. 取得中断类型码N
2. pushf
3. TF = 0, IF = 0
4. pushCS
5. pushIP
6. (IP) = (N*4), (CS) = (N*4+2)
三.中断处理程序的编写
1. 保存用到的寄存器
2. 处理中断
3. 恢复用到的寄存器
4. 用iret指令返回
功能指令:iret
指令说明:pop IP -> pop CS -> popf
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1201_内中断_编写中断处理程序.asm
;程序功能:编写可以显示"overflow!"的中断处理程序.
assumecs:code
codesegment
start:
;第一步:
; -----------------------------------------------------------------------------------
;将程序安装到程序向量表中
;即将由do0至doend段中的代码复制到向量表中
mov ax, cs
mov ds, ax
mov si,offset do0 ; 设置ds:si指向源地址
mov ax, 0
mov es, ax
mov di, 200h ;设置es:di指向目的地址
mov cx,offset do0end-offset do0 ;设置cx为传输长度
;常数可以直接相加减,由编译器计算
;标号地址相减可得长度
cld ;设置传输方向为正
repmovsb
;第二步:
;----------------------------------------------------------------------------------
;设置中断向量表
mov ax, 0 ; 0号中断向量表项为0:0
mov es, ax
mov word ptr es:[0*4], 200h ;偏移地址[低] -- 0:0
mov word ptr es:[0*4+2], 0 ;段地址[高] -- 0:2
;----------------------------------------------------------------------------------
mov ax, 4c00h
int21h
;第三步,开始中断处理子程序
; -----------------------------------------------------------------------------------
;将字符串段写在代码段code中,可以防止被覆盖
do0:
jmpshort do0start
db"overflow!"
;-----------------------------------------------------------------------------------
do0start:
mov ax, cs
mov ds, ax
mov si, 202h ;设置ds:si指向字符串
mov ax, 0b800h
mov es, ax
mov di, 12*160+36+2 ;设置es:di指向显存空间的中间位置
mov cx, 9 ;设置cx为字符串长度
s:
mov al, [si]
mov es:[di], al
inc si
add di, 2
loops
mov ax, 4c00h
int21h
do0end:
nop ;空操作,不执行指令
;------------------------------------------------------------------------------------
codeends
endstart
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.04)汇编_王爽_第13章_学习小结
本章内容:
1.如何编写供应用程序调用的中断例程
2.如何使用中断例程实现循环
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
一.BIOS内容补充:
1. BIOS中有许多的中断例程
2. BIOS(基本输入输出系统),主要有以下四方面的内容
01.硬件系统的检测和初始化程序
02.外部中断和内部中断例程
03.用于对硬件设备进行I/O操作的中断例程
04.其他和硬件系统相关的中断例程
二.开机过程中与中断例程相关的
1.开机 -> CPU通电 -> CS:IP = 0FFFFH:0从CS:IP开始执行
2. ->建立BIOS支持的中断向量 ->登记安装入口
3. ->调用int19进行操作系统引导
4. ->中断例程装入内存
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1302_编写供应用程序调用的中断例程.asm
;程序功能:将一个全是字母,以零结尾的字符串转化为大写
assume cs:code
datasegment
db'conversation',0
dataends
codesegment
start:
mov ax, data
mov ds, ax
mov si, 0
int7ch
mov ax, 4c00h
int21h
codeends
endstart
;--------------------------------------------------------------------
;下面是中断程序 int 7ch的安装程序
assume cs:code
codesegment
start:
;----------------------------------------------------------------------
;第一步:将程序代码复制到向量表中
mov ax, cs
mov ds, ax
mov si,offsetcapital
mov ax, 0
mov es, ax
mov di, 200h
mov cx,offsetcapitalend- offsetcapital
cld
repmovsb
;--------------------------------------------------------------------
;第二步:设置中断向量,指定入口
mov ax, 0
mov es, ax
mov word ptr es:[7ch*4], 200h
mov word ptr es:[7ch*4+2], 0
mov ax, 4c00h
int21h
;----------------------------------------------------------------------
;第三步:需要在向量表执行的工作的代码
capital:
push cx
push si
change:
mov cl, [si]
mov ch, 0
jcxz,ok
and byte ptr [si], 11011111b
inc si
jmpshort change
ok:
pop si
pop cx
iret
capitalend:
nop
codeends
endstart
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1303_使用中断例程实现程序的循环.asm
;程序功能:在屏幕中间显示80个'!'
assume cs:code
codesegment
start:
mov ax, 0b800h
mov es, ax
mov di, 160*12
mov bx,offset s- offset se ; 设置从标号se到标号s的转移位置
mov cx, 80
s:
mov byte ptr es:[di],'!'
add di, 2
int7ch ;如果(cx)!= 0,则转移到标号s处
se:
nop
mov ax, 4c00h
int21h
codeends
endstart
; 7ch的中断例程:
;------------------------------------------------------------------------------------------------
assume cs:code
codesegment
start:
;第一步:复制代码至中断向量表
; -------------------------------------------------------------------------------------------------
mov ax, cs
mov ds, ax
mov si,offset lp
mov ax, 0
mov es, ax
mov di, 200h
mov cx,offset lpend- offset lp
cld
repmovsb
;第二步:设置中断向量表入口
; -------------------------------------------------------------------------------------------------
mov ax, 0
mov es, ax
mov word ptr es:[7ch*4], 200h
mov word ptr es:[7ch*4+2], 0
mov ax, 4c00h
int21h
;第三步:编写需要执行的中断代码
;---------------------------------------------------------------------------------------------------
lp:
push bp ;先将需要用到的bp放入堆栈中
mov bp, sp ;将现在的sp移到bp中
dec cx ; cx减1,直到cx为0不再循环
jcxzlpret
add [bp+2], bx ; 将bp+2,因为刚刚程序开始时执行过push操作
;所以要想获取IP,则需+2,(每push一次,减2)
;改变原程序中IP的值
;在程序刚刚进来中断程序前,程序隐含执行了一次
; push popf之后是 push CS之后是 pushIP的操作
;此时IP放在堆栈中的最顶处
lpret:
pop bp
iret ; 程序返回前,从堆栈中获取地址返回
; pop IP -> pop CS -> popf
endlp:
nop
; --------------------------------------------------------------------------------------------------
codeends
endstart
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:实验13_编写中断例程_显示字符串.asm
;程序要求:编并安装int 7ch中断例程,功能为显示一个用0结束的字符串,中断例程安装在0:200处。
;程序参数:(dh) =行号,(dl)=列号,(cl) = 颜色,ds:si指向字符串首地址
;原题目提供的程序:
assume cs:code
datasegment
db"welcome tomasm!", 0
dataends
codesegment
start:
mov dh, 10 ;行号
mov dl, 10 ;列号
mov cl, 2 ;颜色
mov ax, data
mov ds, ax
mov si, 0
int7ch
mov ax, 4c00h
int21h
codeends
endstart
;开始安装int 7ch
;-------------------------------------------------------------------------------------------
assume cs:code
codesegment:
start:
push ds
push bp
push dx
push cx
push si
;第一步:将代码复制至中断向量表
;-----------------------------------------------------------------------------------------
mov ax, cs
mov ds, ax
mov si,offsetshowstring
mov ax, 0
mov es, ax
mov di, 200h
mov cx,offsetshowstringend-offsetshowstring
cld
repmovsb
;第二步:在中断向量表安装地址
;--------------------------------------------------------------------------------------------
mov ax, 0
mov es, ax
mov word ptr es:[7ch*4], 200h
mov word ptr es:[7ch*4+2], 0
mov ax, 4c00h
int21h
;第三步:编写需要执行的中断代码
;---------------------------------------------------------------------------------------------------
showstring:
mul dh, 16 ;得出行号的地址
add ax, dl ; 得出行列号的地址
mov bx, ax ;设置bx为显示区的偏移地址
mov ax, ss:[sp] ;提取原来的数据地址
mov ds, ax
mov ax, 0b800h
mov es, ax ;设置es的地址为显示缓冲区的段地址
; mov ax, ss:[sp+8] ;提取需要显示的颜色
mov ax, 0
mov di, ax
showtime:
;循环准备及判断
mov ch, ds:[di]
mov cl, 0
jcxzshowstringend
;代码执行
mov al, ds:[di]
mov es:[bx], al ; 将字符copy过去
inc bx
mov es:[bx], di ; 将字符属性copy过去
;循环继续及结束判断
inc bx
inc di
jmpshowtime
showstringend:
pop si
pop cx
pop dx
pop bp
pop ds
nop
codeends
endstart
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.04)汇编_王爽_第14章_学习小结
本章内容:
1. 端口的读写
2.CMOS RAM芯片的访问读写
3.shl,shr位移指令
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1404_CMOS_RAM中存储的时间信息.asm
;程序功能:在屏幕中间显示当前的月份
;程序开始
;---------------------------------------------------------------------------------------------------------------------------
assumecs:code
codesegment
start:
;第一步:从CMOS中提取(月份)数据
;--------------------------------------------------------------------------------------------------------------------------------
; CMOS RAM芯片内部有两个端口地址用于读写CMOS RAM
; 70h —— 地址端口,存放要访问的 CMOS RAM单元的地址。
; 71h —— 数据端口,存放从选定的 CMOS RAM单元中读取的数据,或,写入到其中的数据。
;端口的读写指令只有两条:in从端口读取数据 out从端口写入数据(in out对象只能是ax或者是al)
; CMOS RAM中存放单元:秒(0),分(2),时(4), 日(7),月(8),年(9)
mov al, 8
out70h, al ; al的数值为8,使用out指令,将要访问的CMOS单元地址写入到70h中
;以上,指明,CPU通过地址线跟CMOS RAM端口说,CPU将从8号单元中读出数据
inal, 71h ; 将71h中读入一个字节到al中
mov ah, al ; al中为从CMOS RAM的8号单元中读出的数据
mov cl, 4
shr ah, cl ; ah中为月份的十位数码值
; shl指令,将目标操作数的二进制数值左移N位,
; shr指令,将目标操作数的二进制数值右移N位.
;空位补零,最后移出位是0还是1写入到标志寄存器中的CF
and al, 00001111b ; al中为月份的个位数码值
;第二步:将数据转换为ASCII码
;---------------------------------------------------------------------------------------------------------------------------
;将BCD码转化成十进制相对应的ASCII码
; BCD码值 =十进制数码值
; BCD码值 + 30h =十进制数对应的ASCII码
add ah, 30h
add al, 30h
;第三步:将提取出的月份在显示缓冲区中显示
;----------------------------------------------------------------------------------------------------------------------------
mov bx, 0b800h
mov es, bx
mov byte ptr es:[160*12 +40*2], ah ;显示月份的十位数码
mov byte ptr es:[160*12+40*2+2], al ;接着月份的个位数码
;-------------------------------------------------------------------------------------------------------------------------------
;程序返回
mov ax, 4c00h
int21h
;程序结束:
;---------------------------------------------------------------------------------------------------------------------------------
codeends
endstart
;-----------------------------------------------------------------------------------------------------------------------------------
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:实验14_访问CMOS_RAM.asm
;程序功能:以“年/月/日 时:分:秒"的格式,显示当前的日期,时间。
;------------------------------------------------------------------------------------------------the whole program start---
assumecs:code, ds:timeds, ss:tempa
; -------------------------------------------------------------------------------------------statementstart ---
;段名:ds:timed
;功能: 用于存放时间的数据
;大小:共占用了12个节字
;数据:存放的数据依次是年月日时分秒
; ---------statement end------------------------
timedssegment
db 12dup('0')
timedsends
;--------------------------------------------------------------------------------------------program end---
; -------------------------------------------------------------------------------------------statementstart ---
;段名:ss:tempa
;功能: 用于存放调用程序地址的数据
;大小:共占用了六个字
; ---------statement end------------------------
tempasegment
dw 6dup('0')
tempaends
;--------------------------------------------------------------------------------------------program end---
codesegment
start:
mov ax, tempa ; 设置堆栈地址为程序设定的堆栈内存地址
mov ss, ax
mov ax, 12
mov sp, ax
mov bx, 0 ;依次访问并放入数据进数据段
mov al, 9 ; 年
callraptd
mov al, 8 ;月
callraptd
mov al, 7 ;日
callraptd
mov al, 4 ;时
callraptd
mov al, 2 ;分
callraptd
mov al, 0 ;秒
callraptd
mov ax, 0B800h
mov bx, 0
mov es, ax
mov ax, timeds
mov ds, ax
mov ax, 0
mov di, ax
mov cx, 12 ; 循环显示于显示缓冲区中
lst:
mov ax, ds:[di] ;装入数据
mov es:[bx], ax
inc bx
mov ax, 01110001B ;显示的属性:白底蓝字
mov es:[bx], ax
inc bx
inc di
looplst
mov ax, 4c00h
int21h
; -------------------------------------------------------------------------------------------statementstart ---
;子程序名称:raptd-- read and push data
;子程序功能:将CMOSRAM中的数据提取出来并且放入timesds内存单元中
;子程序需要的参数:al:需要从外部调入(提取RAM的地址)
; ---------statement end------------------------
raptd: ; read and push data
push ax
mov ax, timeds ;设置存放数据的地址
mov es, ax
pop ax
mov ah, 0
out70h, al
inal, 71h ; 先提取出需要的数据
mov es:[bx], al ;将数据存入指定地址中
callexchange
ret
;--------------------------------------------------------------------------------------------program end---
; -------------------------------------------------------------------------------------------statementstart ---
;子程序名称:exchange
;子程序功能:将timesds内存单元中的数据从BCD码转换为ASCII码存放
;子程序需要的参数:bx:每存放一次增加数位2
; al:需要从外部调入(提取RAM的地址)
; ---------statement end------------------------
exchange:
mov al, es:[bx]
mov ah, al
mov cl, 4
shr ah, cl ;十位
and al, 00001111b ;个位
add ah, 30h
add al, 30h
mov es:[bx], ah
inc bx
mov es:[bx], al
inc bx
inc bx
ret
;--------------------------------------------------------------------------------------------program end---
codeends
endstart
; ------------------------------------------------------------------------------------------------the whole program end---
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.07)汇编_王爽_第15章_学习小结
本章内容:
1. 外中断的概念及应用
2. PC机对键盘的处理过程。
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
一。什么是外中断
1. PC向外设的输出也不是直接送入外设,而是先送入端口中,再由相关芯片送到外设。
即:CPU通过端口和外部设备进行联系。
2. 外中断信息,来源于CPU外部,当CPU外部有需要处理的事情发生时,CPU处理完当前的指令后,可以检测到发送过来的中断信息,引发中断过程,处理外设的输入。
3. 外中断源,分为两种:
第一种是:可屏蔽中断,当IF = 0时不响应可屏蔽中断,IF = 1则执行完当前指令后响应。
第二种是:不可屏蔽中断,CPU执行完当前指令后必须立即响应。
二。 PC机对键盘的处理.
1.每按下一个键,芯片会产生一个送到主板寄存器端口为60h的扫描码。
2.通码:按下键时,产生的扫描码。
3.断码:松开键时,产生的扫描码。
4.断码 =通码 + 80h
5.键盘输入到达60h端口时,CPU会从相关芯片中接到断类型码为9的可屏蔽中断信息。
6.系统启动后,BIOS键盘缓冲区中,一个字单元存放一个键盘输入,高字节:扫描码,低字节:字符码。
三。 CPU对键盘输入的处理过程:
1. 键盘产生扫描码
2. 扫描码送入60h端口
3. 引发9号中断
4. CPU执行int9中断例程处理键盘输入
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1504_编写调用BIOS的int 9例程_响应输入的程序.asm
;程序功能:当输出时遇到Esc的扫描码时,改变显示的颜色后返回
;------------------------------------------------------------------------------------------------the whole program start---
assumecs:code
; -------------------------------------------------------------------------------------------statementstart ---
;段名:ss:stack
;功能: 用于临时保存各个寄存器的数据
;大小:共占用了128个字节,即64个字
; ---------statement end------------------------
stacksegment
db128dup (0)
stackends
;--------------------------------------------------------------------------------------------program end---
;-------------------------------------------------------------------------------------------statementstart ---
;段名: ds:data
;功能:用于保存int9中断例程的入口地址
;大小:共占用两个字,即四个字节
; ---------- statement end --------------------------------
datasegment
dw0, 0
dataends
; -------------------------------------------------------------------------------------------program end ---
codesegment
start:
movax, stack ; 先设置好堆栈的地址
movss, ax ;让ss:sp指向stack段的末端
movsp, 128
movax, data ; 然后设置ds的地址,让其指向data段
movds, ax ;目的是存放原中断向量表的地址
movax, 0 ; 之后是设置es的地址,令es作为段地址可以指向中断向量表
moves, ax
pushes:[9*4] ; 将中断向量表中int9的地址放入堆栈中
popds:[0] ;再将地址取出放入data段中
pushes:[9*4+2]
popds:[2] ;将原来的int 9中断例程的入口地址保存在ds:0, ds:2单元中
movword ptr es:[9*4],offset int9
moves:[9*4+2], cs ; 在中断向量表中设置新的int 9中断例程的入口地址
movax, 0b800h ;将es指向显示缓存区的地址
moves, ax
movah,'a'
s:
moves:[160*12+40*2], ah ; 将设定的字母移到显示缓存区中
call delay ; 其中调用了delay子程序,目的是让显示延迟
incah
cmpah,'z'
jna s ; s段的作用是显示字母a~z
movax, 0 ; 再次将es设为段地址0,目的是设置中断向量表中的地址
moves, ax
pushds:[0]
popes:[9*4]
pushds:[2]
popes:[9*4+2] ;将中断向量表中int9中断例程的入口恢复为原来的地址
movax, 4c00h ;程序返回,程序结束
int 21h
; --------------------------------------------------------------------------------------------statementstart ---
;子程序名称:delay
;子程序的功能:不断地执行空循环,目的是时间延迟
; ---------------------statement end ---
delay:
pushax
pushdx
movdx, 0 ; 循环次数2X次。
movax, 0
s1:
incdx
addax, dx
cmpax, 1000H ; 此处设置重复的X次
jne s1
s2:
decdx
cmpdx, 0
jne s2
popdx
popax
ret
;-----------------------------------------------------------------------------------------------programend ----
; --------------------------------------------------------------------------------------------statementstart ---
;子程序名称:新int9中断例程段
;子程序功能:设置新的int9例程,并模拟中断过程。
;子程序分析:子程序一共分为四大步骤
; 1. 标志寄存器入栈 2. IF = 0, TF = 0 3. CS、IP入栈
; 4. (IP) = ((ds)*16+0), (CS) = ((ds)*16+2)
; -------------statement end---------------------------------
int9:
pushax
pushbx
pushes
in al, 60h ; 此处为了引发9号中断
;当键盘的输入到达60h端口时
;相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息
;如果IF = 1,则响应中断,转去执行int9中断例程
pushf ; 此步模拟第1步,标志寄存器入栈
pushf ; 此处开始第2步,将IF及TF置零
popbx ;将flag寄存器中的数据从堆栈中取出,并且下面进行修改
andbh, 11111100b ; 将IF和TF置0
pushbx ;第2步结。修改以后再将flag寄存器中的数据放回堆栈中
popf ; 第1步结.
call dword ptr ds:[0] ; 第3步,CS:IP入栈.对int指令进行模拟,调用原来的int9中断指令
cmpal, 1 ; 扫描al的值是否为“ESC“的扫描码
jne int9ret
movax, 0b800h
moves, ax
incbyte ptr es:[160*12+40*2+1];将属性值加1,改变颜色
int9ret:
popes
popbx
popax
iret
;----------------------------------------------------------------------------------------------------programend ----------------
codeends
endstart
;-------------------------------------------------------------------------------------the whole program end ----------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.09)汇编_王爽_第16章_学习小结
本章内容:
1. seg操作符,取得某一标号的段地址。offset操作符,取得某一标号的偏移地址。
2.如何使用数据标号,描述了单元长度的标号。
3.数值+30h =对应字符的ASCII代码。
4.直接定址表
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:实验16_setscreen_编写多个功能子程序的中断例程.asm
;程序功能:1.清屏 2.设置前景色 3.设置背景色 4.向上滚动一行
;------------------------------------------------------------------------------------------------the whole program start---
assumecs:code
; -------------------------------------------------------------------------------------------statementstart ---
;段名:ss:stack
;功能: 用于临时保存各个寄存器的数据
;大小:共占用了128个字节,即64个字
; ---------statement end------------------------
stacksegment
db 128dup (0)
db 128dup (0)
stackends
;--------------------------------------------------------------------------------------------program end---
;-------------------------------------------------------------------------------------------statementstart ---
;段名: ds:data
;功能:用于保存int9中断例程的入口地址
;大小:共占用两个字,即四个字节
; ---------- statement end--------------------------------
datasegment
dw 0, 0
dataends
;-------------------------------------------------------------------------------------------program end ---
codesegment
start2:
;设置中断入口
mov ax, stack ;先设置好堆栈的地址
mov ss, ax ;让ss:sp指向stack段的末端
mov sp, 256
mov ax, data ;然后设置ds的地址,让其指向data段
mov ds, ax ;目的是存放原中断向量表的地址
mov ax, 0 ;之后是设置es的地址,令es作为段地址可以指向中断向量表
mov es, ax
push es:[9*4] ;将中断向量表中int9的地址放入堆栈中
pop ds:[0] ;再将地址取出放入data段中
push es:[9*4+2]
pop ds:[2] ;将原来的int 9中断例程的入口地址保存在ds:0, ds:2单元中
mov word ptr es:[9*4],offsetint9
mov es:[9*4+2], cs ;在中断向量表中设置新的int9中断例程的入口地址
;新的中断入口设置完成
;下面开始主程序
mov ax, 0b800h
mov es, ax
mov ah,'a'
mov bx, 0
mov cx, 2000
sss:
mov es:[bx], ah
calldelay
add bx, 2
loopsss
;主程序执行完
;下面开始将中断向量表的int9中断例程恢复为原来的地址
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
mov ax, 4c00h
int21h
;主程序执行完成。
; --------------------------------------------------------------------------------------------statementstart ---
;子程序名称:delay
;子程序的功能:不断地执行空循环,目的是时间延迟
; ---------------------statement end ---
delay:
push ax
push dx
push cx
mov cx, 9fffh
clop:
mov dx, 0 ; 循环次数2X次。
mov ax, 0
s1:
inc dx
add ax, dx
cmp ax, 9fffH ;此处设置重复的X次
jnes1
s2:
dec dx
cmp dx, 0
jnes2
loopclop
pop cx
pop dx
pop ax
ret
; -----------------------------------------------------------------------------------------------programend ----
;--------------------------------------------------------------------------------------------statementstart ---
;子程序名称:新int9中断例程段
;子程序功能:设置新的int9例程,并模拟中断过程。
;子程序分析:子程序一共分为四大步骤
; 1. 标志寄存器入栈 2. IF = 0, TF = 0 3. CS、IP入栈
; 4. (IP) = ((ds)*16+0), (CS) = ((ds)*16+2)
; -------------statement end---------------------------------
int9:
push ax
push bx
push es
push cx
push ds
inal, 60h ; 此处为了引发9号中断
;当键盘的输入到达60h端口时
;相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息
;如果IF = 1,则响应中断,转去执行int9中断例程
pushf ;此步模拟第1步,标志寄存器入栈
pushf ;此处开始第2步,将IF及TF置零
pop bx ;将flag寄存器中的数据从堆栈中取出,并且下面进行修改
and bh, 11111100b ;将IF和TF置0
push bx ;第2步结。修改以后再将flag寄存器中的数据放回堆栈中
popf ;第1步结.
calldword ptr ds:[0] ; 第3步,CS:IP入栈.对int指令进行模拟,调用原来的int 9中断指令
cmp al, 0Bh
jefunctions ; 等于零
cmp al, 02
jbint9ret ; 小于1
cmp al, 04
jaint9ret ; 大于3
functions:
mov ah, 0 ;扫描码转化成数字
cmp al, 0Bh
jess3
cmp al, 02
jess2
cmp al, 03
jess1
cmp al, 04
jess0
ss0:
inc ah
ss1:
inc ah
ss2:
inc ah
ss3:
mov al, 0101b
callsetscreen
int9ret:
pop ds
pop cx
pop es
pop bx
pop ax
iret
;----------------------------------------------------------------------------------------------------programend ----------------
;----------------------------------------------------------------------------------------statement start ------
;子程序名称:setscreen
;子程序功能:将0.清屏,1.设置前景色,2.设置背景色,3.向上滚动一行四种功能封装在这函数中。
;子程序参数:ah为功能号0,1,2,3, al为颜色值(对于1,2功能的)
; 功能号*2 =对应的功能子程序在地址表中的偏移。
; -----------------------------------------------------------------------------------------statementend -----
setscreen:
jmpshort set
table dw sub1, sub2, sub3, sub4
set:
push bx ;因为下面要用到bx,为了防止对其它子程序造成影响,先将bx放入堆栈中。
cmp ah, 3 ;判断功能号是否大于3,大于3则不执行任何操作
jasret
mov bl, ah
mov bh, 0
add bx, bx ;根据ah中的功能号按*2计算在table中的地址
callword ptr table[bx]; 调用相对应的功能子程序
sret:
pop bx
ret
;---------------------------------------------------------------------------------------------------------------program end -----
;------------------------------------------------------------------------------------------statement start -----
;子程序名称:sub1
;子程序功能:清屏:将显存中当前屏幕中的字符设为空格符。
; --------------------------------------- statement end--------
sub1:
push bx
push cx
push es
mov bx, 0b800h
mov es, bx ;设置显示区域的地址
mov bx, 0
mov cx, 2000 ; 一屏有80*25 = 2000个字符
sub1s:
mov byte ptr es:[bx],' '
add bx, 2 ;高位存放字符,低位存放属性,所以步进为2
loopsub1s
pop es
pop cx
pop bx
ret
;----------------------------------------------------------------------------------------------------------------program end -----
; ---------------------------------------------------------------------------------------------statementstart -----
;子程序名称:sub2
;子程序功能:设置前景色,设置显存中当前屏幕中处于奇地址的属性字节的第0, 1, 2位
; --------------------------------------- statement end--------
sub2:
push bx
push cx
push es
mov bx, 0b800h
mov es, bx
mov bx, 1 ;字符属性区域从下标1开始
mov cx, 2000 ;一屏需要设置2000个属性
sub2s:
and byte ptr es:[bx], 11111000b ;在使用颜色设置时,先将该属性清零
or es:[bx], al ;结合al,设置前景色
add bx, 2 ;步进为2,原因为2个字节存放一个字符
loopsub2s
pop es
pop cx
pop bx
ret
;----------------------------------------------------------------------------------------------------------------program end -----
;---------------------------------------------------------------------------------------------statementstart -----
;子程序名称:sub3
;子程序功能:设置背景色,设置显存中当前屏幕中处于奇地址的属性字节的第4, 5, 6位
; --------------------------------------- statement end--------
sub3:
push bx
push cx
push es
mov cl, 4
shl al, cl ;将属性左移至正确的位置
mov bx, 0b800h
mov es, bx
mov bx, 1 ;字符属性区域从下标1开始
mov cx, 2000 ;一屏需要设置2000个属性
sub3s:
and byte ptr es:[bx], 10001111b ;将背景色属性清零
or es:[bx], al ;结合al,设置背景色
add bx, 2 ;步进为2,原因为2个字节存放一个字符
loopsub3s
pop es
pop cx
pop bx
ret
;----------------------------------------------------------------------------------------------------------------program end -----
;---------------------------------------------------------------------------------------------statementstart -----
;子程序名称:sub4
;子程序功能:使屏幕显示向上滚动一行:依次将第n+1行的内容复制到第n行处:最后一行为空。
; --------------------------------------- statement end--------
sub4:
push cx
push si
push di
push es
push ds
mov si, 0b800h
mov es, si
mov ds, si
mov si, 160 ; ds:si指向第n+1行
mov di, 0 ; es:di指向第n行
;将下一行移上, moves:[di], byte ptr ds:[si]
cld ; 设置正方向递增, DF= 0
mov cx, 24 ;共复制24行
sub4s:
push cx ;此处需要内嵌循环,先将cx存放
mov cx, 160
repmovsb ; 复制160列
pop cx
loopsub4s ; 再复制21行
mov cx, 80
mov si, 0
sub4s1:
mov byte ptr[160*24+si],'' ;将最后一行清空
add si, 2
loopsub4s1
pop ds
pop es
pop di
pop si
pop cx
ret
;----------------------------------------------------------------------------------------------------------------program end -----
codeends
endstart2
;----------------------------------------------------------------------------------------------------------------the whole program end -----
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(2011.11.10)汇编_王爽_第17章_学习小结
本章内容:
1.int9中断例程对键盘输入的处理
2.int16h中断例程读取键盘缓冲区
3.字符串的输入,栈结构的编写
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
一. int9中断例程对键盘输入的处理
1.键盘输入:在CPU执行完int 9中断例程以后,都放到了键盘缓冲区中。
2.键盘缓冲区:有16个字单元,可以存储15个按键的扫描码和对应的ASCII码。
3.键盘缓冲区:用环形队列的结构管理的内存区。FIFO
4.缓冲区的字单元中:高位字节存储扫描码,低位字节存储ASCII码。
二. int16中断例程读取键盘缓冲区
1. BIOS提供了int16进程,读取一个键盘输入,该功能编号为0,读取完毕后,会将该输入从缓冲区删除。
2.指令:mov ah, 0 ||int16 || 结果: (ah) =扫描码, (al) =ASCII码
3.int 16h中断例程的0号功能的工作:
01. 检测键盘缓冲区中是否有数据;
02. 没有则继续做第1步;
03. 读取缓冲区第一个字单元中的键盘输入;
04. 将读取的扫描码送入ah,将读取的ASCII码送入al;
05. 将已读取的键盘输入从缓冲区中删除。
三.字符串的输入
1.字符串存储空间:实际上是一个字符栈,字符栈中所有的字符,从栈底到栈顶,组成一个字符串。
2.应具备的功能:
01.在输入的同时需要显示这个字符串。
02.一般在输入回车符后,字符串输入结束。
03.能够删除已经输入的字符。
3.程序的处理过程:实际上就是一个字符栈。
01.调用int16读取键盘输入
02.如果是字符,则进入字符栈,显示字符栈中的所有字符;继续执行1.
03.如果是退格键,从字符栈中弹出一个字符,显示字符栈中的所有字符:继续执行1.
04.如果是Enter键,向字符栈中压入0,返回。
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;程序名称:1703_接受字符串输入的子程序.asm
;程序功能:按照我们平时的输入习惯,程序能显示正在输入的字符,按退格键删除,按回车键确认
;--------------------------------------------------------------------------------------------------------------the whole program start ---
getstr:
push ax ;子程序开始前,先将ax入栈,防止对其它子程序造成影响
;程序结束时再将ax出栈。
;------------------------------------------------------------------------------statement start ------------------------------------
;子程序名称:getstrs && nochars
;子程序功能:接受一个键盘输入并判断该输入属于哪种类型的输入
; 若为字符,则跳到字符处理的子程序中
; 若为功能键,则判断是退格键还是Enter键,跳到相应的子程序中
; 最后循环本子程序
;-------------------------------------------------------------statement end -----
getstrs:
mov ah, 0 ; int16的读取键盘缓冲区的功能编号为0
int16h ; 使用int16中断例程
cmp al, 20h
jbnochar ; ASCII码小于20h,说明不是字符,跳到nochar标号中
mov ah, 0
callcharstack ; 字符入栈的调用参数是(ah)=0
mov ah, 2
callcharstack ; 显示栈中的字符,入栈后显示字符,调用参数(ah)=2
jmpgetstrs ; 循环进入该子程序输入,直至按入回车键退出
nochars:
cmp ah, 0eh ;退格键的扫描码
jebackspace
cmp ah, 1ch ; Enter键的扫描码
jeenter
jmpgetstrs
;------------------------------------------------------------------------------------------------------program end ------------
;------------------------------------------------------------------------------statement start ------------------------------------
;子程序名称:backspace && enter
;子程序功能:这两个程序分别实现其功能:
; 输入backspace键时,调用charstack子程序的(参数为1)的字符出栈
; 输入enter键时,调用charstack子程序(参数为0)的字符入栈.
;-------------------------------------------------------------statement end -----
backspace:
mov ah, 1
callcharstack ; 字符出栈
mov ah, 2
callcharstack ; 显示栈中的字符
jmpgetstrs
enter:
mov al, 0
mov ah, 0
callcharstack ; 0入栈
mov ah, 2
callcharstack ; 显示栈中的字符
pop ax
ret ; 跳出该getstrs子程序
;------------------------------------------------------------------------------------------------------program end ------------
;------------------------------------------------------------------------------statement start ------------------------------------
;子程序名称:charstack
;子程序功能:使用汇编实现栈
; 先使用一个table作为各功能的数据标号
; 然后使用一个名为top的数据标号记录元素个数,以实现出入读栈顶的功能
; 最后再使用各子程序实现功能
;-------------------------------------------------------------statement end -----
charstack:
jmpshort charstart ;直接跳到charstart标号处,为了不执行非代码段
; table标号与top标号中的数据
table dw charpush, charpop, charshow
top dw 0
charstart: ;程序开始前,先临时保存需要用到的寄存器
push bx
push dx
push di
push es
;比较ah参数是否0~2正确范围内,若不是,则正常跳出子程序
cmp ah, 2
jasret
mov bl, ah
mov bh, 0
add bx, bx
jmpword ptr table[bx]; 获取在该子程序需要执行程序的入口地址
charpush:
mov bx, top
mov [si][bx], al
inc top
jmpsret
charpop:
cmp top, 0
jesret
dec top
mov bx, top
mov al, [si][bx]
jmpsret
charshow:
mov bx, 0b800h
mov es, bx
mov al, 160
mov ah, 0
mul dh
mov di, ax
add dl, dl
mov dh, 0
add di, dx
mov bx, 0
charshows:
cmp bx, top
jnenoempty
mov byte ptr es:[di],' '
jmpsret
noempty:
mov al, [si][bx]
mov es:[di], al
mov byte ptr es:[di+2],''
inc bx
add di, 2
jmpcharshows
sret:
pop es
pop di
pop dx
pop bx
ret
;----------------------------------------------------------------------------------------------------------------the whole program end -----