|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
91 楼』:
【80x86汇编语言学习笔记】
Windows计算器使用小窍门:
在汇编语言学习过程中,需要经常用到计算器(Windows CALC.exe)来计算二进制或是进行数字的对比,将数字分组功能打开,就会看到数字间隔,方便一眼略知位数。
[ Last edited by redtek on 2007-1-7 at 11:50 AM ]
附件
1: 1.GIF (2007-1-8 00:49, 18.51 K, 下载附件所需积分 1 点
,下载次数: 2)
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-8 00:49 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
92 楼』:
【80x86汇编语言学习笔记】
内中断
中断信息可以来自CPU的内部和外部。
对于8086CPU,当CPU内部有下面情况发生时,将产生相应的中断信息:
1) 除法错误 (中断类型码:0)
2) 单步执行 ( 1)
3) 执行 int0 指令 ( 4)
4) 执行 int 指令 ( int n-提供给CPU的中断类型码)
8086CPU 用称为中断类型码的数据来标识中断信息的来源。
中断类型码为一个字节型数据,可以表示256种中断信息的来源。
中断处理程序
根据CPU的设计,中断类型码的作用就是用来定位中断处理程序。
……
中断向量表
CPU用8位的中断类型骊通过中断向量表找到相应的中断处理程序的入口地址。
中断向量表就是中断向量的列表。
中断向量,就是中断处理程序的入口地址。
中断向量表,就是中断处理程序入口地址的列表。
中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口。
CPU只要知道中断类型码,就可将中断类型码作为中断向量表的表项号,定位相应表项,从而得到中断处理程序的入口地址。
内存 0000:0000 到 0000:03E8 的1000个单元中存放着中断向量表。
对于8086CPU,这个入口地址包括段地址和偏移地址,所以一个表项占两个字。高地址字存放段地址,低地址字存放偏移地址。
提问:
1) 4号中断源对应的中断处理程序的入口地址为:(0000:0010)
Quote: | 0000:0000 68 10 A7 00 8B 01 70 00-16 00 A9 03 8B 01 70 00 |
|
2) 存储N号中断源对应的中断处理程序入口的偏移地址的内存单元的地址为:(N*4)
3) 存储N号中断源对应的中断处理程序入口的段地址的内存单元的地址为:(N*4+2)
中断过程
8086CPU在收到中断信息后,所引发的中断过程:
1) 取得中断类型码
2) 标志寄存器的值入栈
3) 设置标志寄存器的第8位TF和第9位IF的值为0
4) CS的内容入栈
5) IP的内容入栈
6) 从内存地址为 中断类型码*4 和 中断类型码*4+2 的两个字单元中读取中断处理程序的入口地址设置IP和CS。
描述中断过程:
1) 取得中断类型码N
2) pushf
3) TF=0 , IF=0
4) push CS
5) push IP
6) (IP)=(N*4), (CS)=(N*4+2)
中断处理程序
CPU随时都可能检测到中断信息,所以中断处理程序必须一直存储在内存某段空间之中。
中断处理程序的入口地址(中断向量)必须存储在对应的中断向量表项中。
中断处理程序的编写步骤:
1) 保存用到的寄存器
2) 处理中断
3) 恢复用到的寄存器
4) 用 iret 指令返回
iret 指令的功能用汇编语法描述为:
pop IP
pop CS
popf
iret 通常和硬件自动完成的中断过程配合使用。
iret指令执行后,CPU回到执行中断处理程序前的执行点继续执行程序。
除法错误中断的处理
CPU执行div等除法指令时,如发生了除法溢出错误,将产生中断类型码为0的中断信息。CPU检测到这个信息,引发中断过程。
编程处理0号中断
编程:当发生了除法溢出错误产生0号中断信息,引发中断过程,CPU执行我们编写的0号中断处理程序。
在屏幕中间显示提示信息: “overflow!” 后,返回到操作系统中。
编写中断处理程序,当中断0发生时:
1) 相关处理
2) 向显示缓冲区送字符串 "overflow!"
3) 返回DOS
8086支持256个中断,实际系统中要处理的中断事件没有达到256个,在中断向量表中有些单元是空的。
从 0000:0200 至 0000:0300 的256个字节的空间所对应的中断向量表项都是空的,操作系统和其他应用程序都不占用。
可以将自己写的中断处理程序扔到内存 0000:0200 处。那么 0000:0200 就是中断入口地址,把它登记在中断向量表的对应表项中。
-d 0:0200
0000:0200 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0210 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0220 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0230 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0240 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0250 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0260 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0270 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
…… 步骤:
1) 编写可以显示 “overflow!” 的中断处理程序
2) 将程序送入内存 0000:0200 处
3) 将程序的入口地址 0000:0200 存储在中断向量表0号表项中
assume cs:code
code segment
start: mov ax,cs
mov ds,ax ; 源
mov si,offset do0
mov ax,0
mov es,ax ; 目标
mov di,200h
mov cx,offset do0end - offset do0
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
do0: jmp short 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
mov cx,9
s: mov al,[si]
mov es:[di],al
inc si
add di,2
loop s
mov ax,4c00h
int 21h
do0end: nop
code ends
end start CMD 不支持直接写屏操作,DOS下支持~:)
当发生除法溢出时,那个字符串就跳了出来,然后返回到dos。
Quote: | C:\Masm50>debug
-a
0AF5:0100 mov ax,1000
0AF5:0103 mov bl,1
0AF5:0105 div bl
0AF5:0107
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B80010 MOV AX,1000
-t
overflow!
AX=1000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 B301 MOV BL,01
-t
AX=1000 BX=0001 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0105 NV UP EI PL NZ NA PO NC
0AF5:0105 F6F3 DIV BL
-t |
|
单步中断
CPU在执行完一条指令后,如果检测到标志寄存器的TF位为1,则产生单步中断,引发中断过程。
单步中断的中断类型码为1,引发的中断过程如下:
1) 取得中断类型码1
2) 标志寄存器入栈,TF、IF设置为0
3) CS、IP入栈
4) (ip)=(1*4) , (cs)=(1*4+2)
如果TF=1,则执行一条指令后,CPU就会转去执行1号中断处理程序。
Debug 工具也是这样单步跟踪的原理。
CPU提供单中断功能的原因:为单步跟踪程序的执行过程提供了实现机制。
响应中断的特殊情况
0AF5:0100 mov ax,1000
0AF5:0103 mov ss,ax
0AF5:0105 mov ax,0
0AF5:0108 mov sp,0
0AF5:010B
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B80010 MOV AX,1000
-t
AX=1000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 8ED0 MOV SS,AX
-t
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=1000 CS=0AF5 IP=0108 NV UP EI PL NZ NA PO NC
0AF5:0108 BC0000 MOV SP,0000
-t
AX=0000 BX=0000 CX=0000 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=1000 CS=0AF5 IP=010B NV UP EI PL NZ NA PO NC 编写0号中断的处理程序
在除法溢出发生时,在屏幕中间显示字符串 "divide rror!",然后返回到DOS。
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do0
mov ax,0h
mov es,ax
mov di,200h
mov cx,offset doend - offset do0
cld
rep movsb
mov ax,0h
mov es,ax
mov word ptr es:[0*4],200h ; 0:200 Int 0
mov word ptr es:[0*4+2],0h
mov ax,4c00h
int 21h
do0: jmp short do1
db "my name is redtek",0
do1: mov ax,cs
mov ds,ax
mov si,202h
mov ax,0b800h
mov es,ax
mov di,160*20+(160-18*2)/2
mov cx,18
mov ah,02h
s: mov al,[si]
mov es:[di],ax
inc si
add di,2
loop s
mov ax,4c00h
int 21h
doend: nop
code ends
end start 修改中断向量前、后对比:
Quote: | C:\Masm50>debug
-d 0:0
0000:0000 68 10 A7 00 8B 01 70 00-16 00 A9 03 8B 01 70 00 h.....p.......p.
0000:0010 8B 01 70 00 B9 06 12 02-40 07 12 02 FF 03 12 02 ..p.....@.......
0000:0020 46 07 12 02 0A 04 12 02-3A 00 A9 03 54 00 A9 03 F.......:...T...
0000:0030 6E 00 A9 03 88 00 A9 03-A2 00 A9 03 FF 03 12 02 n...............
0000:0040 A9 08 12 02 A4 09 12 02-AA 09 12 02 5D 04 12 02 ............]...
0000:0050 B0 09 12 02 0D 02 E1 02-C4 09 12 02 8B 05 12 02 ................
0000:0060 0E 0C 12 02 14 0C 12 02-1F 0C 12 02 AD 06 12 02 ................
0000:0070 AD 06 12 02 A4 F0 00 F0-37 05 12 02 94 2D 00 C0 ........7....-..
-q
C:\Masm50>p240.exe
C:\Masm50>debug
-d 0:0
0000:0000 00 02 00 00 8B 01 70 00-16 00 A9 03 8B 01 70 00 ......p.......p.
0000:0010 8B 01 70 00 B9 06 12 02-40 07 12 02 FF 03 12 02 ..p.....@.......
0000:0020 46 07 12 02 0A 04 12 02-3A 00 A9 03 54 00 A9 03 F.......:...T...
0000:0030 6E 00 A9 03 88 00 A9 03-A2 00 A9 03 FF 03 12 02 n...............
0000:0040 A9 08 12 02 A4 09 12 02-AA 09 12 02 5D 04 12 02 ............]...
0000:0050 B0 09 12 02 0D 02 E1 02-C4 09 12 02 8B 05 12 02 ................
0000:0060 0E 0C 12 02 14 0C 12 02-1F 0C 12 02 AD 06 12 02 ................
0000:0070 AD 06 12 02 A4 F0 00 F0-37 05 12 02 94 2D 00 C0 ........7....-.. |
|
[ Last edited by redtek on 2007-1-9 at 12:26 AM ]
附件
1: 1.GIF (2007-1-9 13:26, 7.25 K, 下载附件所需积分 1 点
,下载次数: 1)
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-9 03:49 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
93 楼』:
【80x86汇编语言学习笔记】
Int 指令
格式: int n
相当于引发一个n号中断的中断过程。
1) 取中断类型码n
2) 标志寄存器入栈,IF=0,TF=0
3) CS、IP入栈
4) (IP)=(n*4), (CS)=(n*4+2)
先运行自编的0号中断程序(安装)并修改中断向量表。(位于92楼 P240.EXE)
然后调用 int 0 引发0号中断:
C:\Masm50>debug
-a
0AF5:0100 int 0
0AF5:0102
-g
C:\Masm50> CPU执行 int 0 指令时,将引发中断过程,执行0号中断处理程序。
而0号中断处理程序的功能就是显示一行字符串(如上:92楼0号中断处理程序)
int 指令的最终功能和call指令相似,都是调用一段程序。
编写供应用程序调用的中断例程
编写、安装中断7ch的中断例程,功能:求一word型数据的平方。
参数:(AX)=要计算的数据
返回:DX、AX中存放结果的高16位和低16位。
求:2*3456^2
assume cs:code
code segment
start: mov ax,3456
int 7ch ; 调用中断 7ch 的中断例程,计算ax中的数据的平方
add ax,ax ; 低位相加
adc dx,dx ; 再加高位和进位
mov ax,4c00h
int 21h
code ends
end start 1) 编写实现平方功能程序
2) 安装程序
3) 设置中断向量表,指定入口地址
安装程序
assume cs:code
code segment
start: mov ax,cs ; 安装代码
mov ds,ax
mov si,offset sqr
mov ax,0
mov es,ax
mov di,200h
mov cx,offset sqrend - offset sqr
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
sqr: mul ax ; 中断7ch例程
iret
sqrend: nop
code ends
end start 执行过程验证
Quote: | C:\Masm50>debug p242.exe
-r
AX=0000 BX=0000 CX=000E DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0000 NV UP EI PL NZ NA PO NC
0B49:0000 B8800D MOV AX,0D80
-u
0B49:0000 B8800D MOV AX,0D80
0B49:0003 CD7C INT 7C
0B49:0005 03C0 ADD AX,AX
0B49:0007 13D2 ADC DX,DX
0B49:0009 B8004C MOV AX,4C00
0B49:000C CD21 INT 21
0B49:000E E270 LOOP 0080
0B49:0010 83C406 ADD SP,+06
0B49:0013 B8C805 MOV AX,05C8
0B49:0016 50 PUSH AX
0B49:0017 8D4680 LEA AX,[BP-80]
0B49:001A 50 PUSH AX
0B49:001B E83E0D CALL 0D5C
0B49:001E 83C404 ADD SP,+04
-t
AX=0D80 BX=0000 CX=000E DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0003 NV UP EI PL NZ NA PO NC
0B49:0003 CD7C INT 7C
-p
AX=4000 BX=0000 CX=000E DX=00B6 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0005 NV UP EI PL NZ NA PO NC
0B49:0005 03C0 ADD AX,AX
-t
AX=8000 BX=0000 CX=000E DX=00B6 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0007 OV UP EI NG NZ NA PE NC
0B49:0007 13D2 ADC DX,DX
-t
AX=8000 BX=0000 CX=000E DX=016C SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0009 NV UP EI PL NZ NA PE NC |
|
CPU执行 int 7ch 指令进入中断例程之前,标志寄存器、当前的CS和IP被压入栈中,在执行完中断例程后,用 iert 指令恢复 int 7ch 执行前的标志寄存器和CS、IP的值。
int 指令和 iret 指令的配合使用与 call 指令和 ret 指令的配合使用具有相似的思路。
编写、安装中断7ch的中断例程。
功能:将一个全是字母,以0结尾的字符串转化为大写。
参数:ds:si指向字符串的首地址
assume cs:code
data segment
db 'conversation',0
data ends
code segment
start: mov ax,data
mov ds,ax
mov si,0
int 7ch ; 调用字符串转大写的中断例程
mov ax,4c00h
int 21h
code ends
end start 7ch 中断例程安装程序
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset capital
mov ax,0
mov es,ax
mov di,200h
mov cx,offset capitalend - offset capital
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
capital: push cx ; 保存寄存器值,避免冲突
push si
change: mov cl,[si]
mov ch,0
jcxz ok ; 遇到字符串尾0,则转向结束
and byte ptr [si],11011111b
inc si
jmp short change
ok: pop si
pop cx
iret
capitalend: nop
code ends
end start 跟踪结果
Quote: | -t
AX=0B49 BX=0000 CX=001F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0003 NV UP EI PL NZ NA PO NC
0B4A:0003 8ED8 MOV DS,AX
-t
AX=0B49 BX=0000 CX=001F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4A IP=0005 NV UP EI PL NZ NA PO NC
0B4A:0005 BE0000 MOV SI,0000
-t
AX=0B49 BX=0000 CX=001F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4A IP=0008 NV UP EI PL NZ NA PO NC
0B4A:0008 CD7C INT 7C
-p
AX=0B49 BX=0000 CX=001F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4A IP=000A NV UP EI PL NZ NA PO NC
0B4A:000A B8004C MOV AX,4C00
-d ds:0
0B49:0000 43 4F 4E 56 45 52 53 41-54 49 4F 4E 00 00 00 00 CONVERSATION....
0B49:0010 B8 49 0B 8E D8 BE 00 00-CD 7C B8 00 4C CD 21 C4 .I.......|..L.!.
0B49:0020 04 C7 86 FE FE 00 00 EB-05 90 FF 86 FE FE A1 56 ...............V
0B49:0030 07 39 86 FE FE 73 7D 8B-9E FE FE D1 E3 D1 E3 8B .9...s}.........
0B49:0040 87 BE 22 0B 87 C0 22 74-E1 8B 9E FE FE D1 E3 D1 .."..."t........
0B49:0050 E3 8B 87 BE 22 8B 97 C0-22 89 86 FA FE 89 96 FC ...."...".......
0B49:0060 FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05 .....&.G.*.@P...
0B49:0070 0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50 ..RP..F...P....P |
|
[ Last edited by redtek on 2007-1-9 at 12:05 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-9 23:32 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
94 楼』:
【80x86汇编语言学习笔记】
对 int、iret和栈的深入理解
) 用 7ch 中断例程完成 jmp near ptr s 指令的功能,用bx向中断例程传送转移位移。
在屏幕的第 12 行显示 data 段中,以 0 结尾的字符串。
调用者:
assume cs:code
data segment
db 'conversation',0
data ends
code segment
start: mov ax,data
mov ds,ax
mov si,0
mov ax,0b800h
mov es,ax
mov di,160*12
s: cmp byte ptr [si],0
je ok
mov al,[si]
mov es:[di],al
mov es:[di].1,al
inc si
add di,2
mov bx,offset s - offset ok
int 7ch
ok: mov ax,4c00h
int 21h
code ends
end start 中断例程:
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do0
mov ax,0
mov es,ax
mov di,200h
mov cx,offset doend - offset do0
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
do0: push bp
mov bp,sp
add [bp+2],bx ; 设置IP位移量
pop bp
iret
doend: nop
code ends
end start ) 编程:用7ch中断例程完成loop指令的功能。
在屏幕中间显示80个‘!’。
assume cs:code
code segment
start: mov ax,0b800h
mov es,ax
mov di,160*12
mov bx,offset s - offset se ; 存储模拟loop的转移位移
s: mov byte ptr es:[di],'!'
add di,2
int 7ch
se: nop
mov ax,4c00h
int 21h
code ends
end start 进入中断处理程序之前的压栈次序:
标志寄存器入栈,(TF=0、IF=0);
CS入栈
IP入栈
(IP)=(N*4), (CS)=(N*4+2)
------------------------------------------------------- 16位
最后,栈指针(SP)指向最后入栈的16位内容所在的地址。
7ch中断例程……当CPU进行一系列动作时,其压入IP的内容并不是在调用程序中的 int 7ch 所在的IP。
根据CPU执行指令的原理,当CPU读入IP所指向地址的指令时,CPU会另IP指向一下条指令。
所以,当调用 int 7ch 中断之前,CPU 所压入的 IP 是指向一下条指令的,是为了准备执行完当前 int 指令后返回继续执
行之备。
所以,被压入栈的 IP 就是调用代码中“se: nop”这行 se 标号所在的地址~:)
而且,当要 pop 弹出时,则应逆序要弹出的也是 IP ,所以进入 int 7ch 中断处理程序后,其 SP 一直指向 IP 。
当执行 7ch 中断例程时,调用者已将从 se 到 s 标号的位移(使用 offset 指令计算的)存到了 bx 中。
然后将 CS:IP 指向 s 标号(就是将 CS 指向 s 所在的段地址,让 IP 指向 S 所在的偏移地址就行了)。
当 int 7ch 中断例程内,将栈中所压的 IP 与 CS 值改写以后,当执行 iret 指令时,CPU会自动恢复中断前的寄存器
的值,但这值已被我们修改,所以恢复的就是我们修改过的 CS:IP(它指向了 s 标号处)。
下面是调试出错的代码,当 dec cx (递减)时,cx=0 时再递减就发生了为负值,结果进入了死循环。
Quote: | AX=B800 BX=FFF7 CX=0000 DX=0000 SP=FFFA BP=0000 SI=0000 DI=078A
DS=0B39 ES=B800 SS=0B49 CS=0000 IP=0200 NV UP DI PL NZ NA PO NC
0000:0200 55 PUSH BP
-t
AX=B800 BX=FFF7 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=078A
DS=0B39 ES=B800 SS=0B49 CS=0000 IP=0201 NV UP DI PL NZ NA PO NC
0000:0201 8BEC MOV BP,SP
-t
AX=B800 BX=FFF7 CX=0000 DX=0000 SP=FFF8 BP=FFF8 SI=0000 DI=078A
DS=0B39 ES=B800 SS=0B49 CS=0000 IP=0203 NV UP DI PL NZ NA PO NC
0000:0203 49 DEC CX
-t
AX=B800 BX=FFF7 CX=FFFF DX=0000 SP=FFF8 BP=FFF8 SI=0000 DI=078A
DS=0B39 ES=B800 SS=0B49 CS=0000 IP=0204 NV UP DI NG NZ AC PE NC
0000:0204 E304 JCXZ 020A |
|
原来在 int 7ch 中多写了一个 dec cx ,jczx 的判断为0语句被夹在中间,这样它永远也不能正确。
调用者:
assume cs:code
code segment
start: mov ax,0b800h
mov es,ax
mov di,160*12
mov bx,offset s - offset se ; 模拟loop的转移位移
mov cx,80
s: mov byte ptr es:[di],'!'
mov byte ptr es:[di].1,cl ; 变换不同颜色
add di,2
int 7ch
se: nop
mov ax,4c00h
int 21h
code ends
end start 7ch 中断例程安装
assume cs:code
code segment
start: mov ax,cs ; int 7ch 例程安装
mov ds,ax
mov si,offset do0
mov ax,0
mov es,ax
mov di,200h
mov cx,offset doend - offset do0
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
do0: push bp ; int 7ch 中断例程
mov bp,sp
dec cx
jcxz lpret ; 当 cx 值递减至0时结束,准备返回调用者
add [bp+2],bx ; 修改 IP 位移
; 调用者 se + bx中与 s标号处之间的位移,就是s的偏移量
; 因为栈中又压入了 bp(占16位,2字节),所以+2指向栈底方向的IP
lpret: pop bp
iret
doend: nop
code ends
end start ) 上面的内容中,用 7ch 中断例程实现 loop 的功能,上面的 7ch 中断例程所能进行的最大转移位移是多少?
因为 loop 位移是 short 型段内短转移指令。
它的位移量是有符号数,最大位移范围是: -128 ~~ + 127
运行界面
[ Last edited by redtek on 2007-1-9 at 09:46 PM ]
附件
1: 1.GIF (2007-1-10 07:38, 11.38 K, 下载附件所需积分 1 点
,下载次数: 1)
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-10 07:38 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
95 楼』:
【80x86汇编语言学习笔记】
BIOS和DOS所提供的中断例程
在系统板的ROM中存放着一套程序,称为BIOS(基本输入输出系统),BIOS中主要包括:
1) 硬件系统的检测和初始化程序
2) 外部中断和内部中断的中断例程
3) 用于对硬件设备进行 I/O 操作的中断例程
4) 其他和硬件系统相关的中断例程。
操作系统DOS也提供了中断例程。
从操作系统角度来看DOS的中断例程就是操作系统向程序员提供的编程资源。
BIOS和DOS所有提供的中断例程中包含了许多子程序。程序员在编程的时候可以用int指令直接调用中断例程来完成工作。
BIOS和DOS中断例程的安装过程:
1) 开机后,CPU加电,初始化(CS)=0FFFFH,(IP)=0。
自动从FFFF:0单元开始执行程序。
FFFF:0处有一条跳转指令,CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。
2) 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。
对于BIOS所提供的中断例程,只需将入口地址登记在中断向量表中即可,因它是固化到ROM中的程序,一直在内存中。
3) 硬件系统检测和初始化完成后,调用 int 19h 进行操作系统的引导。从此将计算机交由操作系统控制。
4) DOS启动后,除完成其他工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量。
BIOS中断例程应用
一个供程序调用的中断例程中往往包括多个子程序,中断例程内部用传递进来的参数来决定执行哪一个子程序。
BIOS和DOS提供的中断程序,都用AH来传递内部子程序的编号。
int 10h 中断例程设置光标位置
assume cs:code
code segment
start: mov ah,2 ; 调用10号中断例程2号子程序: 置光标位置
mov bh,0 ; 0页
mov dh,5 ; 行号
mov dl,12 ; 列号
int 10h
int 20 ; 行: 0~24 列: 0~79
mov ax,4c00h
int 21h
code ends
end start 置光标位置用在批处理里,可以将光标定位在屏幕的任何地方。
再使用 SET /P=要显示的文字 <NUL 就可以完成类似“填表格”等特殊显示应用。
关于BIOS或是DOS功能调用专门有手册可以查阅,除特殊情况基本都不用记背。
bh 中的页号的含义,显示缓冲区分为8页。(80*25彩色字符模式)每页 160每行 * 25行 = 4000 字节。
int 10h 中断例程在当前光标位置处写属性和字符
assume cs:code
code segment
start: mov ah,9 ; 调用10h号中断例程9号子程序: 在当前光标位置处写属性和字符
mov al,'a' ; AL=要显示的字符; BH=页号; BL=字符属性; CX=写字符个数
mov bh,0
mov bl,26
mov cx,3
int 10h
mov ax,4c00h
int 21h
code ends
end start 在屏幕的5行12列显示红底高亮闪烁(cmd全屏下闪烁无效)绿色的'a'。
assume cs:code
code segment
start: mov ah,2
mov bh,0
mov dh,5
mov dl,12
int 10h
mov ah,9
mov al,'a'
mov bl,11001010b
mov bh,0
mov cx,10
int 10h
mov ax,4c00h
int 21h
code ends
end start 颜色属性
Quote: | 7 6 5 4 3 2 1 0
---------------------------------------------------------------
BL R G B I R G B
------ -------------- ----- ---------------
闪烁 背 景 高亮 前 景
R:红色 G:绿色 B:蓝色 |
|
DOS中断例程应用
int 21h中断例程是DOS提供的中断例程,其中包含了DOS提供给程序员在编程时调用的子程序。
int 21h 中断例程 4ch 号功能,结束程序(带返回码结束)。
mov ah,4ch
mov al,0
int 21h int 21h中断例程在光标位置显示字符串功能
在屏幕的5行12列显示字符串:“Welcome to masm”。
assume cs:code
data segment
db 'Welcome to masm','$'
data ends
code segment
start: mov ah,2 ; To 光标:5,12列
mov bh,0
mov dh,5
mov dl,12
int 10h
mov ax,data
mov ds,ax
mov dx,0
mov ah,9
int 21h
mov ax,4c00h
int 21h
code ends
end start 5行(是指从0~5),12例(是指从0~12)
$ 本身不显示,起边界作用。
编写应用中断例程
编写并安装 int 7ch 中断例程,功能为显示一个用0结束的字符串,中断例程安装在0:200处。
参数:(DH)=行号,(DL)=列号,(CL)=颜色,DS:SI指向字符串首地址。
inc 7ch 安装程序
assume cs:code
code segment
start: mov ax,cs ; 安装
mov ds,ax
mov si,offset do0
mov ax,0
mov es,ax
mov di,200h
mov cx,offset doend - offset do0
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
do0: push dx ; int 7ch
push cx ; 参数:(DH)=行号,(DL)=列号,(CL)=颜色
push ax
push di
push es
mov si,0
mov ax,0b800h
mov es,ax
mov ax,160
mul dh
mov di,ax
mov dh,0
add dx,dx
add di,dx
s: mov ch,[si]
cmp ch,0
je send
mov es:[di],ch
mov es:[di].1,cl
inc si
add di,2
jmp short s
send: pop es
pop di
pop ax
pop cx
pop dx
iret
doend: nop
code ends
end start 调用者
assume cs:code
data segment
db "welcome to masm!",0
data ends
code segment
start: mov dh,10 ; 行号
mov dl,10 ; 列号
mov cl,2 ; 颜色
mov ax,data
mov ds,ax
mov si,0
int 7ch
mov ax,4c00h
int 21h
code ends
end start Debug 跟踪观察 int、iret指令执行前后CS、IP和栈中的状态。
编写并安装 int 7ch 中断例程,功能为完成 loop 指令的功能。
参数:(CX)=循环次数, (BX)=位移
在屏幕中间显示80个‘!’。
调用者:
assume cs:code
code segment
start: mov ax,0b800h
mov es,ax
mov di,160*12
mov bx,offset s - offset se
mov cx,80
s: mov byte ptr es:[di],'!'
add di,2
int 7ch
se: nop
mov ax,4c00h
int 21h
code ends
end start int 7ch 安装
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si, offset do0
mov ax,0
mov es,ax
mov di,200h
mov cx,offset doend - offset do0
cld
rep movsb
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
do0: push bp
mov bp,sp
dec cx
jcxz doreturn
add [bp+2],bx
doreturn: pop bp
iret
doend: nop
code ends
end start 需要注意的地方
当使用 BP 寄存器时寻址时,所操作的是 SS:[ ]
当使用非 BP 寄存器寻址时,所操作的默认为 DS:[ ] 或指定。
ADD [BP+02],BX
Quote: | AX=B800 BX=FFF7 CX=004F DX=0000 SP=FFF8 BP=FFF8 SI=0000 DI=0782
DS=0B39 ES=B800 SS=0B49 CS=0000 IP=0206 NV UP DI PL NZ AC PO NC
0000:0206 015E02 ADD [BP+02],BX SS:FFFA=0017
-d ss:fffa
0B49:FFF0 17 00 49 0B 06 32 ..I..2 |
|
分别在屏幕的第2、4、6、8行显示四句英文诗,补全程序
Quote: | assume cs:code
code segment
s1: db 'Good,btter,best,','$'
s2: db 'Never let it rest,','$'
s3: db 'Till good is better,','$'
s4: db 'And better,best.','$'
s: dw offset s1,offset s2,offset s3,offset s4
row: db 2,4,6,8
start: mov ax,cs
mov ds,ax
mov bx,offset s
mov si,offset row
xor ax,ax
mov cx,4
ok: mov bh,0
mov dh,[si] ; 行号
mov dl,0 ; 0列
mov ah,2
int 10h
mov dx,[bx]
mov ah,9
int 21h
inc si ; 下移指针
add bx,2
loop ok
mov ax,4c00h
int 21h
code ends
end start |
|
[ Last edited by redtek on 2007-1-10 at 04:24 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-10 10:50 |
|
|
pengfei
银牌会员
积分 1218
发帖 485
注册 2006-7-21 来自 湖南.娄底
状态 离线
|
『第
96 楼』:
Quote: | Originally posted by redtek at 2006-12-10 00:48:
【80x86 汇编语言学习笔记】 —— 一句话学习总结
) 找一本认为最难的书学习,更是为了总结最适合自己的学习方法。
只有读那些看上去 “最难的” 书,才最能挖掘更适合自己的有效学习规律与方法。
) 读看一本很难读的书,才最容易看到自己思想上与能力上的弱点。
) 学与悟的重点放在书上所讲的原理,就等于拥有了创造一万种方法的灵感与能力。
) 无论是解读汇编语言还是解读任何一本书,其实都是在悟读着自己。
) 某个阶段,读到认为掌握了很多,那就是并没有学透多少;读到好象什么都不会了,那是已经读透了部分,要再继续。
) 读适合启发自己思维方式的书,读这样的书才能加倍成长。
) 基础知识就是巨厦的地基,它的深浅有一半注定了最终的计算机水平,它的另一半是对数学的理解。
) 跟着书读是白读。先拉后跟的读是真读。用自己的思考方式一边超前预想一边读,会把见解与书的思想碰撞,这才是读。
) 学习某种编程语言的过程,如果发现只有死记硬背才能继续学下去,那么这个时候就意味着基础没有掌握好,应去补基础。
) 把需要死记硬背才能向下学习的过程,分解成不需要记忆照样可以学下去的过程,那就是掌握它的原理深度来降低记忆强度。 |
|
看了兄写的这段学习总结, 感慨良多, 每一点都命中要害.
以前我读书吊儿郎当, 很容易满足, 自学的一年半年里慢慢地找到了学习的方法, 更重要的是磨练了自己的意志, 如果能够做到为了掌握一个知识点连续作战十几个小时, 还有什么困难可以难倒你.
Redtek兄说的对, 学习重在理解, 做事做人都在于领悟, 也许苦学几载不如一朝顿悟.
过去一失败就垂头丧气, 现在觉得失败或许也是成功的预兆, 要善于失败之后找出自己的不足, 设法弥补它.
思想是重要的, 做任何事情, 自己的决心和态度往往是成败的关键.
开弓没有回头箭, 坚持下去你就是最强的, 最后送兄一句:
得意失意, 切莫在意. 顺境逆境, 切莫止境!
|
业精于勤而荒于嬉,形成于思而毁于随。 |
|
2007-1-10 11:42 |
|
|
GOTOmsdos
铂金会员
C++启程者
积分 5154
发帖 1827
注册 2003-7-18
状态 离线
|
『第
97 楼』:
支持!
我学C的情景跟redtek兄学汇编差不多,千军万马中冲出一条血路!
|
|
2007-1-10 12:01 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
98 楼』:
“得意失意, 切莫在意. 顺境逆境, 切莫止境! ”,欣赏~~~
听pengfei兄讲学习感受和GOTOmsdos兄的鼓励真是受益非浅~:)))
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-10 22:17 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
99 楼』:
【80x86汇编语言学习笔记】
端口
各种存府器都和CPU的地址线、数据线、控制线相连。
CPU在操控它们的时候,把它们都当作内存来对待,把它们总的看做一个由若干存储单元组成的逻辑存储器,称其为内存地址空间。
PC机系统中,和CPU通过总线相连的芯片除各种存储器外,还有以下3种芯片:
1) 各种接口卡(网卡、显卡、……)上的接口芯片,它们控制接口卡进行工作
2) 主板上的接口芯片,CPU通过它们对部分外设进行访问
3) 其他芯片,用来存储相关的信息,或进行相关的输入输出处理
从CPU角度,将这些寄存器都当作端口,对它们进行统一编址,从而建立了一个统一端口地址空间。每个端口在地址空间中都有一个地址。
CPU可以直接读写3个地方的数据:
1) CPU内部的寄存器
2) 内存单元
3) 端口
端口的读写
访问端口时CPU通过端口地址来定位端口,端口所在芯片和CPU通过总线相连。端口地址和内存地址一样,通过地址总线来传送。
CPU最多可以定位64K个不同的端口。端口地址的范围为: 0~65535。
in 和 out。
访问端口:
in al,60h ; 从60h号端口读入一个字节
执行时与总线相关的操作:
1) CPU通过地址线将地址信息60H发出
2) CPU通过控制线发出端口读命令,选中端口所在的芯片,并通知它,将要从中读取数据
3) 端口所在的芯片将60H端口中的数据通过数据线送入CPU
在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端口写入一个字节
CMOS RAM芯片
0~0DH单元保存时间信息,其余大部分用于保存系统配置信息,供系统启动时BIOS程序读取。
芯片内部有两个端口,端口地址为70H和71H。CPU通过这两个端口写CMOS RAM。
70H为地址端口,存放要访问的CMOS RAM单元地址
71H为数据端口,存放从选定的CMOS RAM单元中读取的数据。
CPU对CMOS RAM的读写分两步进行。
例:读CMOS RAM的2号单元:
1) 将2送入端口70H
2) 从71H读出2号单元的内容
读取CMOS RAM的2号单元内容
mov al,2
out 70h,al
in al,71h 向CMOS RAM的2号单元写入0
C:\Masm50>debug
-a
0AF5:0100 mov al,2
0AF5:0102 out 70,al
0AF5:0104 mov al,0
0AF5:0106 out 71,al CMOS RAM中存储的时间信息
CMOS RAM 各字节的含义(部分):
===============================================================
偏移值(Offset) 数据字段的意义描述(Description)
---------------------------------------------------------------
00h 目前系统时间的“秒数”字段
01h 预约警铃时间的“秒数”字段
02h 目前系统时间的“分钟”字段
03h 预约警铃时间的“分钟”字段
04h 目前系统时间的“小时”字段
05h 预约警铃时间的“小时”字段
06h 星期几(星期一=01,星期二=02,依次类推)
07h 目前系统日期字段(0~31)
08h 系统公元纪年的后两位(00~99;00=2000,01=2001,以此类推)
(关于上面BIOS更详细的内容,传说中有一本书,研读完了可以自己开发BIOS,哈哈……)
(关于这本书的介绍: 《BIOS研发技术剖析》 )
Quote: | 【内容简介】
本书是全球第一本针对新一代计算机平台的BIOS核心技术做深入剖析的技术性书籍,内附完整的BIOS源代码以及相关的技术文献,完备程度超过二十年前那本IBM BIOS Bible,将这个业界研发技术缺口至少拉近了十八、九年,所以本书的出现,绝对可以说是难得的创举! 本书不仅着重于BIOS的结构剖析,还指导读者如何制作及修改BIOS,更难能可贵的是本书附赠了最重要的源程序代码,让读者能实际动手做BIOS。本书主要针对具有基本汇编语言基础,熟悉DOS操作指令且从事软件开发、编程的读者编写而成。 |
|
索引CMOS内容对照(摘自互联网,原作者不详)
Quote: | CMOS内容对照
地址 内容 地址 内容 地址 内容 地址 内容
00h Time - Seconds 20h Reserved 40h Extended CMOS 60h User Password
01h Alarm - Seconds 21h Reserved 41h Extended CMOS 61h User Password
02h Time - Minutes 22h Reserved 42h Extended CMOS 62h Extended CMOS
03h Alarm - Minutes 23h Reserved 43h Extended CMOS 63h Extended CMOS
04h Time - Hours 24h Reserved 44h Extended CMOS 64h Extended CMOS
05h Alarm - Hours 25h Reserved 45h Extended CMOS 65h Extended CMOS
06h Date - Day of the week 26h Reserved 46h Extended CMOS 66h Extended CMOS
07h Date - Day 27h Reserved 47h Extended CMOS 67h Extended CMOS
08h Date - Month 28h Reserved 48h Extended CMOS 68h Extended CMOS
09h Date - Year 29h Reserved 49h Extended CMOS 69h Extended CMOS
0Ah Status Register A 2Ah Reserved 4Ah Extended CMOS 6Ah Extended CMOS
0Bh Status Register B 2Bh Reserved 4Bh Extended CMOS 6Bh Extended CMOS
0Ch Status Register C 2Ch Reserved 4Ch Extended CMOS 6Ch Extended CMOS
0Dh Status Register D 2Dh Reserved 4Dh Extended CMOS 6Dh Extended CMOS
0Eh Diagnostic Status 2Eh CMOS Checksum (high byte) 4Eh Extended CMOS 6Eh Extended CMOS
0Fh Shutdown Status 2Fh CMOS Checksum (low byte) 4Fh Extended CMOS 6Fh Extended CMOS
10h A; 30h Extended Memory (high byte) 50h Extended CMOS 70h Extended CMOS
11h Reserved 31h Extended Memory (low byte) 51h Extended CMOS 71h Extended CMOS
12h 0 32h Date - Century 52h Extended CMOS 72h Extended CMOS
13h Reserved 33h Power On Status 53h Extended CMOS 73h Extended CMOS
14h Equipment Installed 34h Reserved 54h Extended CMOS 74h Extended CMOS
15h Base Memory (high byte) 35h Reserved 55h Extended CMOS 75h Extended CMOS
16h Base memory (low byte) 36h Reserved 56h Extended CMOS 76h Extended CMOS
17h Extended Memory (high byte) 37h Reserved 57h Extended CMOS 77h Extended CMOS
18h Extended Memory (low byte) 38h Reserved 58h Extended CMOS 78h Extended CMOS
19h 0 (C:) Hard Disk Type 39h Reserved 59h Extended CMOS 79h Extended CMOS
1Ah 1 (D:) Hard Disk Type 3Ah Reserved 5Ah Extended CMOS 7Ah Extended CMOS
1Bh Reserved 3Bh Reserved 5Bh Extended CMOS 7Bh Extended CMOS
1Ch Supervisor Password 3Ch Reserved 5Ch Extended CMOS 7Ch Extended CMOS
1Dh Supervisor Password 3Dh Reserved 5Dh Extended CMOS 7Dh Extended CMOS
1Eh Reserved 3Eh Reserved 5Eh Extended CMOS 7Eh Extended CMOS
1Fh Reserved 3Fh Reserved 5Fh Extended CMOS 7Fh Extended CMOS |
|
在 CMOS RAM中,存放着当前的时间:年、月、日、时、分、秒。这6个信息的长度都为1个字节,存放见上面列表。
这些数据以 BCD 码的方式存放。
BCD码是以4位二进制数表示十进制数码的编方法。
例:数值 26,用BCD码表示为:0010 0110
一个字节可以表示两个BCD码。
CMOS RAM存储时间信息的单元中,存储了用两个BCD码表示的两位十进制数,高4位BCD码表示十位,低4位BCD码表示个位。
例:00010100B表示14。
编程:在屏幕中间显示当前的月份。
assume cs:code
code segment
start: mov al,8 ; 读 CMOS RAM 8号单元
out 70h,al
in al,71h
mov ah,al ; 分离取出的BCD码为十位和个位
mov cl,4
shr ah,cl
and al,00001111b
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
int 21h
code ends
end start 编程:以 “年/月/日 时:分:秒” 的格式,显示当前日期、时间。
;---------------------------------------------------
;
; 年/月/日 时:分:秒
;---------------------------------------------------
assume cs:code
data segment
db 9,8,7,4,2,0 ; (年月日时分秒)在CMOS中的地址
db '/','/',' ',':',':' ; 日期时间间隔符号
data ends
code segment
start: mov ax,20h
mov ss,ax
mov sp,16
mov ax,data
mov ds,ax
mov ax,0b800h
mov es,ax
mov si,0
mov di,0
xor ax,ax
mov cx,6
s: mov al,[si] ; 读CMOS信息
out 70h,al
in al,71h
mov ah,al ; 转BCD码
push cx
mov cl,4
shr ah,cl
pop cx
and al,00001111b
add ah,30h
add al,30h
mov byte ptr es:[160*12+30*2+di],ah
mov byte ptr es:[160*12+30*2+di].2,al
mov al,ds:[si+6] ; 显示日期与时间间隔符号
mov es:[160*12+30*2+di].4,al
inc si
add di,6
loop s
mov ax,4c00h
int 21h
code ends
end start [ Last edited by redtek on 2007-1-11 at 08:36 PM ]
附件
1: 1.GIF (2007-1-12 09:35, 6.59 K, 下载附件所需积分 1 点
,下载次数: 1)
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-11 05:30 |
|
|
lxmxn
版主
积分 11386
发帖 4938
注册 2006-7-23
状态 离线
|
『第
100 楼』:
好久没有来兄这里看了,惭愧……
好想和兄一起学习汇编,但是由于学业比较繁重,很多课程都来不及搞,所以只有先把汇编放一放了,等有时间再来和兄一起学习汇编,呵呵,恐怕到那时候兄已经是汇编牛人了,^_^
|
|
2007-1-11 09:29 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
101 楼』:
等兄有时间了我们一起学习更多更好玩的东东~:)
经常看到批处理区兄的精彩代码,水平越来越高,真是为之高兴~:)
希望经常看到兄的创造性思想和好玩的代码~:)))
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-11 23:35 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
102 楼』:
【80x86汇编语言学习笔记】
逻辑移位指令
shl和Shr逻辑移位指令。
shl 左移
shr 右移
SHL逻辑左移指令,功能:
1) 将一个寄存器或内存单元中的数据向左移位
2) 将最后移出的一位写入CF中
3) 最低位用0补充
指令:
assume cs:code
code segment
start: mov al,01001000b
shl al,1
mov ah,4ch
int 21h
code ends
end start 结果:
Quote: | C:\Masm50>p256.exe
C:\Masm50>echo %errorlevel%
144 |
|
返回值144(十进制)它的二进制是:10010000B ,左移了一位~:)
最后移出的一位写入CF中
assume cs:code
code segment
start: mov al,10010000b
shl al,1
mov ah,4ch
int 21h
code ends
end start 红色标示CF
绿色标示结果
Quote: | C:\Masm50>debug p256.exe
-r
AX=0000 BX=0000 CX=0008 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0000 NV UP EI PL NZ NA PO NC
0B49:0000 B090 MOV AL,90
-u
0B49:0000 B090 MOV AL,90
0B49:0002 D0E0 SHL AL,1
0B49:0004 B44C MOV AH,4C
0B49:0006 CD21 INT 21
0B49:0008 8D867AFE LEA AX,[BP+FE7A]
0B49:000C 50 PUSH AX
0B49:000D E8E270 CALL 70F2
0B49:0010 83C406 ADD SP,+06
0B49:0013 B8C805 MOV AX,05C8
0B49:0016 50 PUSH AX
0B49:0017 8D4680 LEA AX,[BP-80]
0B49:001A 50 PUSH AX
0B49:001B E83E0D CALL 0D5C
0B49:001E 83C404 ADD SP,+04
-t
AX=0090 BX=0000 CX=0008 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0002 NV UP EI PL NZ NA PO NC
0B49:0002 D0E0 SHL AL,1
-t
AX=0020 BX=0000 CX=0008 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0004 OV UP EI PL NZ NA PO CY |
|
如果移动位数大于1时,必须将移动位数放在CL中。
assume cs:code
code segment
start: mov al,01010001b
mov cl,3
shl al,cl
mov ah,4ch
int 21h
code ends
end start 左移了3位,值:10001000B
右移,与左移shl操作相反。
1) 将一个寄存器或内存单元中的数据向右移位
2) 将最后移出的一位定入CF中
3) 最高位用0补充
将X逻辑左移一位,相当于执行 X=X*2
将X逻辑右移一位,相当于执行 X=X/2
编程:用加法和移位指令计算(AX)=(AX)*10
加法运算(AX)=(AX)*10
运算 3*10
assume cs:code
code segment
start: mov ax,0
mov cx,10
s: add ax,3
loop s
mov ah,4ch
int 21h
code ends
end start 结果
Quote: | C:\Masm50>p258.exe
C:\Masm50>echo %errorlevel%
30 |
|
移位指令计算(AX)=(AX)*10
计算 3*10
X左移1位就相当于X*2
X左移2位………… X*4
X左移3位………… X*8
(AX)*10=(AX)*2+(AX)*8
(AX)*10=(AX)左移1位+(AX)左移3位
assume cs:code
code segment
start: mov al,3
mov cl,3
shl al,cl
add al,3
add al,3
mov ah,4ch
int 21h
code ends
end start [ Last edited by redtek on 2007-1-11 at 12:08 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-11 23:50 |
|
|
ccwan
金牌会员
积分 2725
发帖 1160
注册 2006-9-23 来自 河北廊坊
状态 离线
|
『第
103 楼』:
这样的网志堪称经典了,向兄学习。
|
三人行,必有吾师焉。 学然后知不足,教然后知困,然后能自强也。 |
|
2007-1-12 06:54 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
104 楼』:
有ccwan兄的不断鼓励,我准备把这网志写溢出了,哈哈……
[ Last edited by redtek on 2007-1-11 at 08:44 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-12 09:39 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
105 楼』:
【80x86汇编语言学习笔记】
外中断
接口芯片和端口
在PC系统的接口卡和主板上,装有各种接口芯片。
这些外设接口芯片的内部有若干寄存器,CPU将这些寄存器当作端口来访问。
外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中。
CPU向外设输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。
CPU还可以向外设输出控制命令,而这些控制命令也是先送到相关芯片的端口中,然后再由相关的芯片根据命令对外设实施控制。
CPU通过端口和外部设备进行联系。
外中断信息
在PC系统中,外中断源一共有两类:
1) 可屏蔽中断
可屏蔽中断中CPU可以不响就的外中断。
IF=1,则CPU在执行完当前指令后响应中断,引发中断过程。
IF=0,则不响应可屏蔽中断。
IF位于FLAG第9位。
关于中断所引发的中断过程:
1) 取中断类型码N
2) 标志寄存器入栈 IF=0,TF=0
3) CS、IP入栈
4) (IP)=(N*4), (CS)=(N*4+2)
可屏蔽中断所引发的中断过程,除与第1步实现上有所不同外,基本和内中断的中断过程相同。
因为可屏蔽中断信息来自于CPU外部,中断类型是通过数据总线送入CPU的。
而内中断的中断类型码是在CPU内部产生的。
将IF置0的原因:在进入中断处理程序后,禁止其他的可屏蔽中断。
如果在中断处理程序中需要处理可屏蔽中断,可以用指令将IF置1:
8086CPU提供设置IF指令:
STI 用于设置IF=1 (允许响应中断)
CLI 用于设置IF=0 (不响应中断)
2) 不可屏蔽中断
不可屏蔽中断是CPU必须响应的外中断。
对于8086CPU,不可屏蔽中断的中断类型码固定为2。
所以中断过程中,不需要取中断类型码。
不可屏蔽中断的中断过程为:
1) 标志寄存器入栈 IF=0, TF=0;
2) CS、IP入栈
3) (IP)=8, (CS)=(0AH)
几乎所有由外设引发的外中断,都是可屏蔽中断。
PC机键盘的处理过程
键盘输入的处理过程:
1) 键盘产生扫描码
2) 扫描码送入60H端口
3) 引发9号中断
4) CPU执行INT9中断例程处理键盘输入
上面第1、2、3步均由硬件完成。
BIOS键盘缓冲区是系统启动后,BIOS用于存放INT9中断例程所接收键盘输入的内存区,可存储15个键盘输入。
INT9中断例程除了接收扫描码外,还要产生和扫描码对应的字符码,所以BIOS键盘缓冲区中,一个键盘输入用一个字单元存放。
高位字节存放扫描码,低位字节存放字符码。
0040:17 单元存储键盘状态字节,该字节记录了控制键和切换键的状态。
显示从 a ~~ z :
assume cs:code
code segment
start: mov ax,0b800h
mov es,ax
mov ah,'a'
s: mov es:[160*12+40*2],ah
inc ah
cmp ah,'z'
jna s
mov ax,4c00h
int 21h
code ends
end start 简单延时-循环
assume cs:code
code segment
start: mov dx,0afffh
mov ax,0ffffh
s: sub ax,1
sbb dx,0
cmp ax,0
jne s
cmp dx,0
jne s
mov ax,4c00h
int 21h
code ends
end start 显示从 a ~~ z 字母,每隔一定时间显示下一个字母
assume cs:code
stack segment
db 128 dup (0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,128
mov ax,0b800h
mov es,ax
mov ah,'a'
s: mov es:[160*12+40*2],ah
call delay
inc ah
cmp ah,'z'
jna s
mov ax,4c00h
int 21h
delay: push ax ; 循环延时
push dx
mov dx,1000h
mov ax,0
s1: sub ax,1
sbb dx,0
cmp ax,0
jne s1
cmp dx,0
jne s1
pop dx
pop ax
ret
code ends
end start 键盘输入到达60H端口后,就会引发9号中断,CPU则转去执行INT9中断例程。要求编写INT9中断例程,功能如:
1) 从60H端口读出键盘的输入
2) 调用BIOS的INT9中断例程,处理其他硬件细节
3) 判断是否为ESC键扫描码,如果是,改变显示的颜色后返回;
如果不是则直接返回。
编写自己的INT9中断例程,但原INT9有一些复杂的硬件细节处理,所以在我们自己的INT9中断例程中还要调用原INT9。
所以,在中断向量表内将INT9中断例程入口地址改写为我们自己的中断例程前,需要先保存原INT9。
当保存原INT9中断例程的偏移地址和段地址后,再调用它时就不能使用INT9来调用了。需要用别的指令来对INT指令进行模拟。
在用其它指令对INT指令进行模拟前,需要先看一下INT指令在执行的时候CPU是如何工作的:
1) 取中断类型码 N
2) 标志寄存器入栈
3) IF=0、 TF=0
4) CS、IP入栈
5) (IP)=(N*4), (CS)=(N*4+2)
取中断类型码是为了定位中断例程的入口地址。
但是,当我们将原INT9中断入口地址保存到另一个内存区域时,我们就不需要中断类型码了,因为我们知道那块自己指定的在内存在哪。
假设已将原INT9中断例程的入口地址保存到了 ds:0 与 ds:2 单元中。现在模拟int过程:
1) 标置寄存器入栈
2) IF=0、TF=0
3) CS、IP入栈
4) (IP)=((DS)*16+0) , (CS)=((CS)*16+2)
上面第3步是CS、IP入栈,因为如果不入栈调用完中断或是子程序就回不到调用者了。
不过,如果是引发INT中断例程的话,CPU是会帮我们做保存CS与IP(入栈)的,所以不用我们自己来。
可是,现在不是要调用INT中断例程,因为原INT9已被我们备份到另一个地址(它的入口地址已经不在中断向量表项中了)。所以我们如果需要调用INT9(就是要执行它,然后再返回到调用者处)就必须使用call命令。
而CALL命令有一个特点:执行CALL命令时,IP指向CALL命令下一条指令(为的是将来返回调用者继续执行CALL后面的指令)。如果CALL是段内CALL,则CPU将IP压栈(不用我们压,这个过程是CPU自动完成的);如果CALL是段间(即:CS:IP)方式,则CPU会自动压入CS、IP(都要压入,不然回不来了)。
所以,上面的CALL的过程和INT调用过程在压入CS、IP入栈的过程是一模一样的。
CALL与调用中断例程不一样的地方是:调用中断例程时CPU自动先压标志寄存器,然后……
而CALL执行时CPU压IP或是CS:IP,但它并不压标志寄存器入栈。
所以如果想用CALL来模拟调用中断例程的方式使用存在另一块内存地址的 DS:0 和 DS:2 的原INT9中断例程,则需要我们:
(1) 自己压入标志寄存器
(2) 自己设置IF=0,TF=0(这些得自己做,CALL指令不会帮我们做的,只有INT调用CPU才会帮我们做)
(3) 使用 call dword ptr ds:[0] 的方式(4字)调用原INT9入口地址,执行原INT9例程。
如何将标志寄存器入栈?
pushf指令可以实现。
如何将标志寄存器的IF与TF的值设置为0?
IF 标志寄存器的第9位
TF 标志寄存器的第8位
关于IF与TF的含义:
IF 表示可屏蔽中断
TF 表示单步中断
为什么要禁止单步中断?
当TF=1(允许单步中断)时,CPU在执行完一条指令后将此发单步中断,转去执行中断处理程序。
而中断处理程序也是由一条条指令组成,如果在执行中断处理程序之前,TF=1,则CPU在执行完中断处理程序的第一条指令后又要产生单步中断,则又要转去执行单步中断的中断处理程序,在执行完中断处理程序的第一条指令后,又要产生单步中断,则又要转去执行单步中断的中断处理程序……无穷无尽的下去……
所以,有时候必须将TF=0(设置为禁卡单步中断),这样才能正确的连续的执行需要的过程。
不可屏蔽的中断类型码固定为:2
中断过程中不需要取中断类型码。不可屏蔽中断过程的寻址部分:
(IP)=(8), (CS)=(0AH)
其实上面的计算过程如下原理:
(IP)=(2*4)
(CS)=(2*4+2)
不可屏蔽中断是在系统中有必须处理的紧急情况发生时用来通知CPU的中断信息。
编程:在屏幕中间依次显示 a~~z ,延时处理显示在屏幕上。
在显示过程中,按下 Esc 键后,改变显示颜色。
assume cs:code
stack segment
db 128 dup (0)
stack ends
data segment
dw 0,0
data ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
mov ax,0
mov es,ax
push es:[9*4] ; 备份原int 9中断例程入口地址
pop ds:[0]
push es:[9*4+2]
pop ds:[2]
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
mov ax,0b800h ; 显示从 'a'~'z' 字母
mov es,ax
mov ah,'a'
s: mov es:[160*12+40*2],ah
call delay ; 延时
inc ah
cmp ah,'z'
jna s
mov ax,0
mov es,ax
push ds:[0] ; 恢复原int9中断向量表项中的入口地址
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
mov ax,4c00h
int 21h
delay: push ax ; 延时子程序
push dx
mov dx,1000h
mov ax,0
s1: sub ax,1
sbb dx,0 ; 再计算高位并减去借位
cmp ax,0
jne s1
cmp dx,0
jne s1 ; 当 ax 与 dx 值都为0时才算递减尽
pop dx
pop ax
ret
int9: push ax ; ------------------------------
push bx ; 自定义 int 9 中断例程
push es ; ------------------------------
in al,60h
pushf ; 备份标志寄存器的值
pushf ; 压入标志寄存器用来修改IF、TF值
pop bx
and bh,11111100b
push bx
popf ; 弹出修改好的flag值,修改当前标志寄存器值
call dword ptr ds:[0]
cmp al,1 ; 比对是否按了 Esc 键 (Esc 键扫描码: 01 )
jne int9ret
mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1] ; 改变颜色
int9ret: pop es
pop bx
pop ax
iret
code ends
end start ) 分析上面int9中断例程,精简指令
进入中断例程后,IF和TF都已经置0。
pushf
pushf
pop ax
and ah,11111100b
push ax
popf
call dword ptr ds:[0]
上面批令可以精简为:
pushf
call dword ptr ds:[0]
精减后的全部代码如下:
assume cs:code
stack segment
db 128 dup (0)
stack ends
data segment
dw 0,0
data ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
mov ax,0
mov es,ax
push es:[9*4] ; 备份原int 9中断例程入口地址
pop ds:[0]
push es:[9*4+2]
pop ds:[2]
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
mov ax,0b800h ; 显示从 'a'~'z' 字母
mov es,ax
mov ah,'a'
s: mov es:[160*12+40*2],ah
call delay ; 延时
inc ah
cmp ah,'z'
jna s
mov ax,0
mov es,ax
push ds:[0] ; 恢复原int9中断向量表项中的入口地址
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
mov ax,4c00h
int 21h
delay: push ax ; 延时子程序
push dx
mov dx,1000h
mov ax,0
s1: sub ax,1
sbb dx,0 ; 再计算高位并减去借位
cmp ax,0
jne s1
cmp dx,0
jne s1 ; 当 ax 与 dx 值都为0时才算递减尽
pop dx
pop ax
ret
int9: push ax ; ------------------------------
push bx ; 自定义 int 9 中断例程
push es ; ------------------------------
in al,60h
pushf ; 备份标志寄存器的值
call dword ptr ds:[0]
cmp al,1 ; 比对是否按了 Esc 键 (Esc 键扫描码: 01 )
jne int9ret
mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1] ; 改变颜色
int9ret: pop es
pop bx
pop ax
iret
code ends
end start ) 分析上面主程序
在主程序中,如果执行设置INT9中断例程的段地址和篇移地址的指令之间发生了键盘中断,
则CPU将转去一个错误的地址并执行,将发生错误。
排除潜在错误的完全代码:
assume cs:code
stack segment
db 128 dup (0)
stack ends
data segment
dw 0,0
data ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
mov ax,0
mov es,ax
cli ; 不响应可屏蔽中断
push es:[9*4] ; 备份原int 9中断例程入口地址
pop ds:[0]
push es:[9*4+2]
pop ds:[2]
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
sti ; 响应可屏幕中断
mov ax,0b800h ; 显示从 'a'~'z' 字母
mov es,ax
mov ah,'a'
s: mov es:[160*12+40*2],ah
call delay ; 延时
inc ah
cmp ah,'z'
jna s
mov ax,0
mov es,ax
cli
push ds:[0] ; 恢复原int9中断向量表项中的入口地址
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
sti
mov ax,4c00h
int 21h
delay: push ax ; 延时子程序
push dx
mov dx,1000h
mov ax,0
s1: sub ax,1
sbb dx,0 ; 再计算高位并减去借位
cmp ax,0
jne s1
cmp dx,0
jne s1 ; 当 ax 与 dx 值都为0时才算递减尽
pop dx
pop ax
ret
int9: push ax ; ------------------------------
push bx ; 自定义 int 9 中断例程
push es ; ------------------------------
in al,60h
pushf ; 备份标志寄存器的值
call dword ptr ds:[0]
cmp al,1 ; 比对是否按了 Esc 键 (Esc 键扫描码: 01 )
jne int9ret
mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1] ; 改变颜色
int9ret: pop es
pop bx
pop ax
iret
code ends
end start
编程:安装新的 int 9 中断例程,使得原 int 9 中断例程的功能得到扩展。
在DOS下,按F1键后改变当前屏幕的显示颜色,其他的键照常处理。
assume cs:code
stack segment
db 128 dup (0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,128
push cs
pop ds
mov ax,0
mov es,ax
mov si,offset int9
mov di,204h
mov cx,offset int9end - offset int9
cld
rep movsb
push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]
cli
mov word ptr es:[9*4],204h
mov word ptr es:[9*4+2],0
sti
mov ax,4c00h
int 21h
int9: push ax
push bx
push cx
push es
in al,60h
pushf
call dword ptr cs:[200h]
cmp al,3bh
jne int9ret
mov ax,0b800h
mov es,ax
mov bx,1
mov cx,2000
s: inc byte ptr es:[bx]
add bx,2
loop s
int9ret: pop es
pop cx
pop bx
pop ax
iret
int9end: nop
code ends
end start 在 Windows CMD 下调试先运行安装程序。然后进入一个16位程序界面(DEBUG、EDIT等),然后在里面按F1键有效。
此代码正常运行环境: MS-DOS
编程:安装新的 int 9 中断例程
在DOS下,按下 “A” 键后,除非不再松开,如果松开,就显示满屏幕 “A” ,其他键照常处理。
按下一个键时产生的扫描码称为通码。
松开一个键时产生的扫描码称为断码。
断码=通码+80H
assume cs:code
stack segment
db 128 dup (0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,128
push cs
pop ds
mov si,offset int9
mov ax,0
mov es,ax
mov di,204h
mov cx,offset int9end - offset int9
cld
rep movsb
push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]
cli
mov word ptr es:[9*4],204h
mov word ptr es:[9*4+2],0
sti
mov ax,4c00h
int 21h
int9: push ax
push bx
push cx
push es
in al,60h
pushf
call dword ptr cs:[200h]
add al,80h
cmp al,1eh
jne int9iret
mov ax,0b800h
mov es,ax
mov bx,0
mov cx,2000
s: mov byte ptr es:[bx],'A'
add bx,2
loop s
int9iret: pop es
pop cx
pop bx
pop ax
iret
int9end: nop
code ends
end start 上面的代码原理从个人理解角度讲,已经可以做好玩的东东了~:)
指令系统总结
1 数据传送指令
2 算术运算指令
3 逻辑指令
4 转移指令
5 处理机控制指令
6 串处理指令
[ Last edited by redtek on 2007-1-13 at 11:06 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-12 09:44 |
|
|