所有的程序都在笔者的机器上跑通了,大家不用担心程序有问题。
环境:Windows 10,DOSBOX,masm
本人是一名Javaer,汇编程序看起来实在是头疼,虽然无法使用面向对象的思想,但将汇编程序进行结构化可读性还是能提高不少的,所以我将书中的程序都进行了‘结构化’。
有的同学可能会觉得和书上的程序不是一模一样,觉得不爽,这部分同学可以自己改一下,如果自己不愿意动手,肯定是学不好的。
1、基础知识
检测点1.1
(1) 13
解析:8 = 2 ^ 3,1KB = 1024字节 = 2 ^ 10字节,8KB = [ (2 ^ 3 ) * (2 ^ 10) ] B = (2 ^13)B,寻址单位为字节,所以地址总线宽度最少为13位,才能具备寻址8KB的能力
(2) 1024,0 ~ 1023
解析:1KB = 1024B ,编号从0开始
(3) 8192,1024
解析:1 KB = 1024 Byte ,1 Byte = 8 bit
(4)102410241024, 1024*1024, 1024
解析:1 KB=1024 B,1 MB = 1024 KB ,1 GB = 1024 MB,所以1 GB = (102410241024)B …
(5)64KB , 1MB,16MB , 4GB
解析:2 ^ 16 = 64K , 2 ^ 20 = 1M , 2 ^ 24 = 16 M , 2 ^ 32 = 4 G
(6)1, 1, 2, 2, 4
解析:1根数据线一次传输1bit数据,8根1次就可以传输8bit(1B),16根1次就可以传输16bit(2B),32根1次就可以传输32bit(4B)
(7)512 , 256
解析:
8086数据总线为16位,每次读取2字节,1024 / 2 = 512
80386数据总线为32位,每次读取4个字节,1024 / 4 = 256
(8)二进制0/1
2、寄存器
检测点2.1
assume cs:codesg ; 将寄存器cs和段-codesg联系起来
codesg segment
; 指令 十进制 二进制 十六进制
mov ax,62627 ;ax=62627, 1111 0100 1010 0011, F4A3H
mov ah,31h ;ax=12707, 0011 0001 1010 0011, 31A3H
mov al,23h ;ax=12579, 0011 0001 0010 0011, 3123H
add ax,ax ;ax=25158, 0110 0010 0100 0110, 6246H
mov bx,826ch ;bx=33388, 1000 0010 0110 1100, 826CH
mov cx,ax ;cx=25158, 0110 0010 0100 0110, 6246H
mov ax,bx ;ax=33388, 1000 0010 0110 1100, 826CH
add ax,bx ;ax=1240 , 0000 0100 1101 1000, 04D8H
mov al,bh ;ax=1154 , 0000 0100 1000 0010, 0482H
mov ah,bl ;ax=27778, 0110 1100 1000 0010, 6C82H
add ah,ah ;ax=55426, 1101 1000 1000 0010, D882H
add al,6 ;ax=55432, 1101 1000 1000 1000, D888H
add al,al ;ax=55312, 1101 1000 0001 0000, D810H
mov ax,cx ;ax=25158, 0110 0010 0100 0110, 6246H
codesg ends
end
⚠️ 注意:高8位和低8位寄存器是独立的寄存器,因此al,bl,cl,dl 进行运算时产生的进位,并不会改变高位的值,而是直接丢失。
检测点2.2
(1)00010H ~1000FH
解析:0001:0000 ~ 0001:FFFF,段地址左移4位+偏移地址:
00010H
+ 0000H
= 00010H
00010H
+ FFFFH
= 1000FH
(2)1001 ~2000
⚠️ 注意:20000H - FFFFH = 10001,以这个值得出段地址1000,是访问不到20000H的,所以段地址为1001
检测点 2.3
解析:修改了4次,最后 IP 值为0
执行指令的过程是:1、将指令读入指令缓冲区;2、修改IP,指向下条指令;3、执行指令;
mov ax,bx ;入指令缓冲区,修改 IP,执行指令,结果:ax=bx
sub ax,ax ;入指令缓冲区,修改 IP,执行指令,结果:ax=0
jmp ax ;入指令缓冲区,修改 IP,执行指令,结果:jmp指令修改IP的值为ax的值,即IP=0
实验
第3章 寄存器(内存访问)
本章共30页
问题3.1
检测点 3.1
答案(1)
MOV AX,0001
MOV DS,AX
MOV AX,[0000] ;AX=2662H
MOV BX,[0001] ; BX=E626H
MOV AX,BX ;AX=E626H
MOV AX,[0000] ;AX=2662H
MOV BX,[0002] ; BX=D6E6H
ADD AX,BX ;AX=FD48H
ADD AX,[0004] ;AX=2C14H
MOV AX,0000 ;AX=0000H
MOV AL,[0002] ;AX=00E6H
MOV BX,0000 ; BX=0000H
MOV BL,[000C] ; BX=0026H
ADD AL,BL ;AX=000CH (E6H+26H=10CH ,10CH的最高位溢出,所以AX=000CH)
上机验证过程如下:
(1)、使用下面的命令修改内存中的内容(window xp系统下可以直接复制粘贴)
e 0:0 70
e 0:1 80
e 0:2 f0
e 0:3 30
e 0:4 ef
e 0:5 60
e 0:6 30
e 0:7 e2
e 0:8 0
e 0:9 80
e 0:a 80
e 0:b 12
e 0:c 66
e 0:d 20
e 0:e 22
e 0:f 60
e 0:10 62
e 0:11 26
e 0:12 e6
e 0:13 d6
e 0:14 cc
e 0:15 2e
e 0:16 3c
e 0:17 3b
e 0:18 ab
e 0:19 ba
e 0:1a 0
e 0:1b 0
e 0:1c 26
e 0:1d 6
e 0:1e 66
e 0:1f 88
修改后查看,结果如下
-d 0:0 1f
0000:0000 70 80 F0 30 EF 60 30 E2-00 80 80 12 66 20 22 60 p..0.`0.....f "`
0000:0010 62 26 E6 D6 CC 2E 3C 3B-AB BA 00 00 26 06 66 88 b&....<;....&.f.
(2)、debug下使用a
命令,复制粘贴下列命令(window xp系统下可以直接复制粘贴)
mov ax,1
mov ds,ax
mov ax,[0]
mov bx,[1]
mov ax,bx
mov ax,[0]
mov bx,[2]
add ax,bx
add ax,[4]
mov ax,0
mov al,[2]
mov bx,0
mov bl,[c]
add al,bl
(3)、单步执行命令,结果如下:
AX=0001 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=0105 NV UP EI PL NZ NA PO NC
0ADD:0105 A10000 MOV AX,[0000] DS:0000=2662
-t
AX=2662 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=0108 NV UP EI PL NZ NA PO NC
0ADD:0108 8B1E0100 MOV BX,[0001] DS:0001=E626
-t
AX=2662 BX=E626 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=010C NV UP EI PL NZ NA PO NC
0ADD:010C 89D8 MOV AX,BX
-t
AX=E626 BX=E626 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=010E NV UP EI PL NZ NA PO NC
0ADD:010E A10000 MOV AX,[0000] DS:0000=2662
-t
AX=2662 BX=E626 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=0111 NV UP EI PL NZ NA PO NC
0ADD:0111 8B1E0200 MOV BX,[0002] DS:0002=D6E6
-t
AX=2662 BX=D6E6 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=0115 NV UP EI PL NZ NA PO NC
0ADD:0115 01D8 ADD AX,BX
-t
AX=FD48 BX=D6E6 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=0117 NV UP EI NG NZ NA PE NC
0ADD:0117 03060400 ADD AX,[0004] DS:0004=2ECC
-t
AX=2C14 BX=D6E6 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=011B NV UP EI PL NZ AC PE CY
0ADD:011B B80000 MOV AX,0000
-t
AX=0000 BX=D6E6 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=011E NV UP EI PL NZ AC PE CY
0ADD:011E A00200 MOV AL,[0002] DS:0002=E6
-t
AX=00E6 BX=D6E6 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=0121 NV UP EI PL NZ AC PE CY
0ADD:0121 BB0000 MOV BX,0000
-t
AX=00E6 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=0124 NV UP EI PL NZ AC PE CY
0ADD:0124 8A1E0C00 MOV BL,[000C] DS:000C=26
-t
AX=00E6 BX=0026 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=0128 NV UP EI PL NZ AC PE CY
0ADD:0128 00D8 ADD AL,BL
-t
AX=000C BX=0026 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0001 ES=0ADD SS=0ADD CS=0ADD IP=012A NV UP EI PL NZ NA PE CY
0ADD:012A B80100 MOV AX,0001
(2)
上机验证过程:
(1)、使用debug的a
命令,在指定的内存处输入指令
-a 2000:0
2000:0000 mov ax,6622
2000:0003 jmp 0ff0:100
2000:0008 mov bx,ax
2000:000A
-a 1000:0
1000:0000 mov ax,2000
1000:0003 mov ds,ax
1000:0005 mov ax,[8]
1000:0008 mov ax,[2]
(2)、使用debug的r
命令,修改cs、ip的值
-r cs
CS 0ADD
:2000
-r ip
IP 0100
:0
(3)、使用debug的t
命令,单步执行指令
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0ADD ES=0ADD SS=0ADD CS=2000 IP=0000 NV UP EI PL NZ NA PO NC
2000:0000 B82266 MOV AX,6622
-t
AX=6622 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0ADD ES=0ADD SS=0ADD CS=2000 IP=0003 NV UP EI PL NZ NA PO NC
2000:0003 EA0001F00F JMP 0FF0:0100
-t
AX=6622 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0ADD ES=0ADD SS=0ADD CS=0FF0 IP=0100 NV UP EI PL NZ NA PO NC
0FF0:0100 B80020 MOV AX,2000
-t
AX=2000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0ADD ES=0ADD SS=0ADD CS=0FF0 IP=0103 NV UP EI PL NZ NA PO NC
0FF0:0103 8ED8 MOV DS,AX
-t
AX=2000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=0ADD SS=0ADD CS=0FF0 IP=0105 NV UP EI PL NZ NA PO NC
0FF0:0105 A10800 MOV AX,[0008] DS:0008=C389
-t
AX=C389 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=0ADD SS=0ADD CS=0FF0 IP=0108 NV UP EI PL NZ NA PO NC
0FF0:0108 A10200 MOV AX,[0002] DS:0002=EA66
-t
AX=EA66 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=0ADD SS=0ADD CS=0FF0 IP=010B NV UP EI PL NZ NA PO NC
0FF0:010B 0000 ADD [BX+SI],AL DS:0000=B8
第4章 第一个程序
本章共20页
问题3.3
mov ax,1000
mov ds,ax
mov ax,[0]
mov bx,[2]
mov cx,[1]
add bx,[1]
add cx,[2]
程序4.1
第6章 包含多个段的程序
本章共16页
检测点6.1
(1)参考答案
;------------------------------检测单 6.1 ----------------------
;------- 依次用0:0 ~ 0:15 单元中的内容替换程序中dw定义的数据 -------
;--------------------------------------------------------------
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
start: mov ax,0
mov ds,ax
mov bx,0
mov cx,8
s: mov ax,[bx]
mov cs:[bx],ax
add bx,2
loop s
mov ax,4c00h
int 21h
codesg ends
end start
(2)参考答案
;------------------------------检测单 6.1-2 ------------------------------------
;- 依次用0:0 ~ 0:15 单元中的内容替换程序中的数据 ,使用栈来传送数据 ------------
;-------------------------------------------------------------------------------
assume cs:codesg
codesg segment
;--定义的数据放的初始地址是 (cs:0) --
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
dw 0,0,0,0,0,0,0,0,0,0
start: mov ax,cs
mov ss,ax
mov sp,24h
mov ax,0
mov ds,ax
mov bx,0
mov cx,8
l: push ds:[bx]
pop cs:[bx]
add bx,2
loop l
mov ax,4c00h
int 21h
codesg ends
end start
第7章 更灵活的定位内存地址的方法
7.2、ASCII
编码方案,就是一套规则,规则约定用什么样的信息来表示现实中的对象。比如ASCII编码方案中,用61H来表示现实生活中的字母 ‘a’
使用文本编辑器时,我们按下字母 a,此时会在文本编辑器中显示a,这个过程是怎样的?
1、a对应的编码送入内存
按下键盘上的字母a,键盘会触发一个外部中断 ,BIOS提供的中断处理程序会处理这个中断,该程序将a对应的ASCII编码(61H) 送入内存中的BIOS键盘缓冲区
。
2、文本编辑器取出a编码
文本编辑器从内存中的键盘缓冲区中取出编码,然后送入显卡上的显存中,工作在文本模式下的显卡根据ASCII码规则驱动显示器显示a在屏幕上
7.3 以字符形式给出的数据
mov ax,'a'
db 'ab'
上面的指令等价于下面的指令
mov ax,61h
db 61h,62h
7.4 大小写转换的问题
;| ------------------------------- 7.4 字符串输入 ------------
;| 功能:| 'BaSiC' 转为大写,'iNfOrMaTiOn' 转为小写
;|--------------------------------------------------------------
assume cs:code
datasg segment
db 'BaSiC'
db 'iNfOrMaTiOn'
code segment
main:;{
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5
for_to_uppercase:;{
mov al,[bx]
and al,11011111b
mov [bx],al
inc bx
loop for_to_uppercase
;}
mov cx,11
for_to_lowercase:;{
mov al,[bx]
or al,00100000b
mov [bx],al
inc bx
loop for_to_lowercase
;}
mov ax,4c00h
int 21h
;}
code ends
end main
解析:程序利用大写字母ASCII编码比小写字母ASCII编码少32(20h),且他们的ASCII码恰巧可以使用and 和or运算实现加减运算(不是所有的数都可以用一个and或or来实现加减运算的)。
7.5、[bx+idata]
问题7.1
assume cs:code
code segment
main:;{
call init_data
mov ax,2000h
mov ds,ax
mov bx,1000h
mov ax,[bx]
mov cx,[bx+1]
add cx,[bx+2]
;}
;// 初始化数据
init_data:;{
mov ax,2000h
mov ds,ax
mov byte ptr ds:[1000h],0beh
mov byte ptr ds:[1001h],00h
mov byte ptr ds:[1002h],06h
mov byte ptr ds:[1003h],00h
mov byte ptr ds:[1004h],00h
mov byte ptr ds:[1005h],00h
ret
;}
code ends
end main
问题 7.2
;----------------------------------------------------------------------------------
;------------------------------问题7.2 -------------------------------------------
;- 用si和di将字符串'welcom to masm!'复制到它后面的数据区中--
;----------------------------------------------------------------------------------
assume cs:codesg ,ds:datasg
datasg segment
db 'welcome to masm!'
db '................'
datasg ends
codesg segment
start: mov ax,datasg
mov ds,ax
mov si,0
mov di,16
mov cx,8
l: mov ax,[si]
mov [di],ax
add si,2
add di,2
loop l
codesg ends
end start
问题 7.3
;----------------------------------------------------------------------------------
;------------------------------问题7.3 -------------------------------------------
;- 用si和di将字符串'welcom to masm!'复制到它后面的数据区中--
;----------------------------------------------------------------------------------
assume cs:codesg ,ds:datasg
datasg segment
db 'welcome to masm!'
db '................'
datasg ends
codesg segment
start: mov ax,datasg
mov ds,ax
mov si,0
mov cx,8
l: mov ax,[si]
mov [si+16],ax
add si,2
loop l
codesg ends
end start
问题 7.4
assume cs:code
code segment
main:;{
call init_data
mov ax,2000h
mov ds,ax
mov bx,1000h
mov si,0
mov ax,[bx+si]
inc si
mov cx,[bx+si]
inc si
mov di,si
add cx,[bx+di]
;}
;// 初始化数据
init_data:;{
mov ax,2000h
mov ds,ax
mov byte ptr ds:[1000h],0beh
mov byte ptr ds:[1001h],00h
mov byte ptr ds:[1002h],06h
mov byte ptr ds:[1003h],00h
mov byte ptr ds:[1004h],00h
mov byte ptr ds:[1005h],00h
ret
;}
code ends
end main
问题 7.5
assume cs:code
code segment
main:;{
call init_data
mov ax,2000h
mov ds,ax
mov bx,1000h
mov si,0
mov ax,[bx+2+si]
inc si
mov cx,[bx+2+si]
inc si
mov di,si
mov bx,[bx+2+di]
nop
;}
;// 初始化数据
init_data:;{
mov ax,2000h
mov ds,ax
mov byte ptr ds:[1000h],0beh
mov byte ptr ds:[1001h],00h
mov byte ptr ds:[1002h],06h
mov byte ptr ds:[1003h],00h
mov byte ptr ds:[1004h],6ah
mov byte ptr ds:[1005h],22h
ret
;}
code ends
end main
11 标志寄存器
检测点11.2
复习标志位的意义:
位置 | 英文缩写 | 标志名 | 标志为0 | 标志为1 |
---|---|---|---|---|
11 | OF - Overflow | 溢出(是/否) | NV - No Overflow | OV - Overflow |
10 | DF - Direction | 方向(减量/增量) | UP - Up | DN - Down |
9 | IF - Interrupt | 中断(允许/关闭) | DI - Disable Interrupt 不许 | EI - Enable Interrupt 允许 |
7 | SF - Symbol | 符号(负/正) | PL - Plus 非负 | NG - Negative 负 |
6 | ZF - Zero | 零(是/否) | NZ - No Zero | ZR -Zero |
4 | AF - Auxiliary | 辅助进位(是/否) | NA | AC |
2 | PF - Parity | 奇偶(偶/奇) | PO - Odd 奇数个1 | PE - Parity Even 偶数个1 |
0 | CF - Carry | 进位(是/否) | NC - No Carry | CY - Carry Yes |
⭐️ OF 进行有符号数运算,运算的结果超出了表示范围,此时OF为1
⭐️ CY 进行无符号数运算,运算的结果向比最高有效位的“更高位”(假想位)进位,此时CY为1
解析:
mov 对标志寄存器不产生影响,所以2,4,6,8的标志寄存器值分别和1,3,4,7一样, 所以我们分析一下1,3,4,7指令执行后标志寄存器的值。
序号 | 指令 \ 标志位 | CF进位(NC,CY ) | 溢出OF(NV,OV) | 符号SF(PL,NG) | ZF零(NZ,ZR) | 奇偶PF(PO,PE) |
---|---|---|---|---|---|---|
1 | sub al,al | 0 | 0 | 0 | 1 | 1 |
2 | mov al,10H | 0 | 0 | 0 | 1 | 1 |
序号 | 指令 \ 标志位 | CF进位(NC,CY ) | 溢出OF(NV,OV) | 符号SF(PL,NG) | ZF零(NZ,ZR) | 奇偶PF(PO,PE) |
---|---|---|---|---|---|---|
1 | sub al,al | 0 | 0 | 0 | 1 | 1 |
2 | mov al,10H | 0 | 0 | 0 | 1 | 1 |
3 | add al,90H | 0 | 0 | 1 | 0 | 1 |
4 | mov al,80H | 0 | 0 | 1 | 0 | 1 |
序号 | 指令 \ 标志位 | CF进位(NC,CY ) | 溢出OF(NV,OV) | 符号SF(PL,NG) | ZF零(NZ,ZR) | 奇偶PF(PO,PE) |
---|---|---|---|---|---|---|
1 | sub al,al | 0 | 0 | 0 | 1 | 1 |
2 | mov al,10H | 0 | 0 | 0 | 1 | 1 |
3 | add al,90H | 0 | 0 | 1 | 0 | 1 |
4 | mov al,80H | 0 | 0 | 1 | 0 | 1 |
5 | add al,80H | 1 | 1 | 0 | 1 | 1 |
6 | mov al,0FCH | 1 | 1 | 0 | 1 | 1 |
序号 | 指令 \ 标志位 | CF进位(NC,CY ) | 溢出OF(NV,OV) | 符号SF(PL,NG) | ZF零(NZ,ZR) | 奇偶PF(PO,PE) |
---|---|---|---|---|---|---|
1 | sub al,al | 0 | 0 | 0 | 1 | 1 |
2 | mov al,10H | 0 | 0 | 0 | 1 | 1 |
3 | add al,90H | 0 | 0 | 1 | 0 | 1 |
4 | mov al,80H | 0 | 0 | 1 | 0 | 1 |
5 | add al,80H | 1 | 1 | 0 | 1 | 1 |
6 | mov al,0FCH | 1 | 1 | 0 | 1 | 1 |
7 | add al,05H | 1 | 0 | 0 | 0 | 0 |
8 | mov al,7DH | 1 | 0 | 0 | 0 | 0 |
序号 | 指令 \ 标志位 | CF进位(NC,CY ) | 溢出OF(NV,OV) | 符号SF(PL,NG) | ZF零(NZ,ZR) | 奇偶PF(PO,PE) |
---|---|---|---|---|---|---|
1 | sub al,al | 0 | 0 | 0 | 1 | 1 |
2 | mov al,10H | 0 | 0 | 0 | 1 | 1 |
3 | add al,90H | 0 | 0 | 1 | 0 | 1 |
4 | mov al,80H | 0 | 0 | 1 | 0 | 1 |
5 | add al,80H | 1 | 1 | 0 | 1 | 1 |
6 | mov al,0FCH | 1 | 1 | 0 | 1 | 1 |
7 | add al,05H | 1 | 0 | 0 | 0 | 0 |
8 | mov al,7DH | 1 | 0 | 0 | 0 | 0 |
9 | add al,0BH | 0 | 1 | 1 | 0 | 1 |
第 13 章 int 指令
13.1 int指令
int n
,触发1个中断类型码为n的中断
;------------------------------ 13.1 int指令 -------------------
;功能:屏幕中间显示有个“!”,然后触发一个除法错误
assume cs:code
code segment
start:;{
mov ax,0b800h
mov es,ax
mov byte ptr es:[12*160+40*2],'!'
int 0
mov ax,4c00h
int 21h
;}
code ends
end start
在DOSBOX下不显示Divide overflow,但在Windows xp下会显示(我使用的是虚拟机安装的xp)
13.2 编写功能应用程序调用的中断例程
问题1
本程序需要复习的指令:
cld
, 标志寄存器df位置0
rep 指令
, 循环执行接在rep后面的串传送指令
(cx) 次
movsb
1、将ds:si指向内存单元中的1个字节,送入es:di指向的内存单元
2、根据标志寄存器中df位,将si和di增1(df=0)或减1(df=1)
;|----------------------------- 13.2 问题1 -----------------------------------------------------
;| 功能:| 求一个word型数据的平方
;|-----------------------------------------------------------------------------------------------
;| 参数:| (ax) = 一个word型数据
;|-----------------------------------------------------------------------------------------------
;|返回值:| dx,ax中存放结果的高16位,低16位
;|-----------------------------------------------------------------------------------------------
;| 备注:| 2*3456^2 = 23887872 = 16C8000H
;|-----------------------------------------------------------------------------------------------
assume cs:code
code segment
main:;{
call copy_program
call set_int_table
mov ax,3456
int 7ch
;// (ax的平方) * 2
add ax,ax
adc dx,dx
mov ax,4c00h
int 21h
;}
;// 将`代码`复制到内存0:200处
copy_program:;{
;//设置`程序`的源地址 ds:si()
set_source_addr:;{
mov ax,cs
mov ds,ax
mov si,offset sqr
;}
;//设置`程序`的目的地址 es:di(0:200)
set_dst_addr:;{
mov ax,0
mov es,ax
mov di,200h
;}
;// 循环传输`程序`代码到目的地址
rep_copy:;{
mov cx,offset sqr_end - offset sqr
;// 设置DF标志位为0,表示传输方向为正向
cld
rep movsb ; 循环传输
;}
ret
;}
;// 设置中断向量表。在中断向量表的0:0位置,设置`程序`的入口地址
set_int_table:;{
mov ax,0 ;
mov es,ax ;
mov word ptr es:[7ch*4],200h ; 低位为偏移地址200h
mov word ptr es:[7ch*4+2],0 ; 高位为段地址0
ret
;}
;// 计算ax中数据的平方
sqr:;{
mul ax
iret
sqr_end:
nop
;}
;------------------------------------------------------------------------------------------------
code ends
end main
问题2
如何将小写字母转为大写?我们 点击链接:参看一下ASCII表,小写字母比大写字母的值大32H。那么用小写字母的值减去32H就是大写之母的值了,转换指令就是:
指令 | |
---|---|
小写字母转大写 | and 小写,11011111B |
大写字母转小写 |
安歇
;|----------------------------- 13.2 问题2 -----------------------------------------------------
;| 功能:| 将一个全是字母,以0结尾的字符串,转换为大写
;|-----------------------------------------------------------------------------------------------
;| 参数:| ds:si 指向字符串首地址
;|-----------------------------------------------------------------------------------------------
assume cs:code
data segment
db 'conversation',0
data ends
code segment
main:;{
mov bx,data
call copy_program
call set_int_table
int 7ch
mov ax,4c00h
int 21h
;}
;// 将`代码`复制到内存0:200处
copy_program:;{
;//设置`程序`的源地址 ds:si()
set_source_addr:;{
mov ax,cs
mov ds,ax
mov si,offset capital
;}
;//设置`程序`的目的地址 es:di(0:200)
set_dst_addr:;{
mov ax,0
mov es,ax
mov di,200h
;}
;// 循环传输`程序`代码到目的地址
rep_copy:;{
mov cx,offset capital_end - offset capital
;// 设置DF标志位为0,表示传输方向为正向
cld
rep movsb ; 循环传输
;}
ret
;}
;// 设置中断向量表。在中断向量表的0:0位置,设置`程序`的入口地址
set_int_table:;{
mov ax,0 ;
mov es,ax ;
mov word ptr es:[7ch*4],200h ; 低位为偏移地址200h
mov word ptr es:[7ch*4+2],0 ; 高位为段地址0
ret
;}
;// 计算ax中数据的平方
capital:;{
;// 备份程序中用到的寄存器
back_reg:;{
push ax
push ds
push si
push cx
;}
mov ax,data
mov ds,ax
mov si,0
for_uppercase:;{
mov cl,[si]
mov ch,0
jcxz for_change_end
and byte ptr ds:[si],11011111b
inc si
jmp short for_uppercase
;}
;// 程序结束
for_change_end:;{
pop cx
pop si
pop ds
pop ax
iret
;}
capital_end:
nop
;}
;------------------------------------------------------------------------------------------------
code ends
end main
13.3 int、iret和栈的深入理解
;| ------------------------------- 13.3 7ch中断处理程序完成loop指令的功能 -----------------------
;| 功能:| 在屏幕中显示80个“!”
;|-----------------------------------------------------------------------------------------------
;| 参数:|
;|-----------------------------------------------------------------------------------------------
;|返回值:|
;|-----------------------------------------------------------------------------------------------
;| 备注:|
;|-----------------------------------------------------------------------------------------------
assume cs:code
data segment
db 'conversation',0
data ends
code segment
start:
call copy_program
call set_int_table
mov ax,0b800h
mov es,ax
mov di,160*12
mov bx,offset s - offset s_end ;
mov cx,80
s:
mov byte ptr es:[di],'!'
add di,2
int 7ch
s_end:
nop
mov ax,4c00h
int 21h
;------------------------------------------------------------------------------------------------
copy_program: ;{ //将`代码`复制到内存0:200处
mov ax,cs ; { // 设置`程序`的源地址 ds:si
mov ds,ax ;
mov si,offset loop_sim ; }
mov ax,0 ; { // 设置`程序`的目标地址 es:di
mov es,ax ;
mov di,200h ; }
mov cx,offset loop_sim_end - offset loop_sim ; { 设置循环次数,循环次数= 代码的长度
cld ; 设置DF标志位为0,0-传输方向为正向
rep movsb ; 循环传输
; }
ret ;}
set_int_table: ;{ // 在中断向量表的0:0位置,设置`程序`的入口地址
mov ax,0 ;
mov es,ax ;
mov word ptr es:[7ch*4],200h ; 低位为偏移地址200h
mov word ptr es:[7ch*4+2],0 ; 高位为段地址0
ret ;}
;------------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------------
loop_sim: ;见上图解释
push bp
mov bp,sp
dec cx
jcxz loop_ret
add [bp+2],bx
loop_ret:
pop bp
iret
loop_sim_end:
nop
;------------------------------------------------------------------------------------------------
code ends
end start
13.6 BIOS 中断例程应用
;| ------------------------------- 13.6 -----------------------
;| 功能:| 屏幕第5行12列显示3个红底,高亮闪烁的绿色'a'
;|-----------------------------------------------------------------------------------------------
;| 参数:|
;|-----------------------------------------------------------------------------------------------
;|返回值:|
;|-----------------------------------------------------------------------------------------------
;|-----------------------------------------------------------------------------------------------
assume cs:code
code segment
start:
mov ah,2 ; ah存中断例程的“内部子程序编号”
mov bh,0
mov dh,5
mov dl,12
int 10h
mov ah,9
mov al,'a'
mov bh,0
mov bl,11001010b
mov cx,3
int 10h
mov ax,4c00h
int 21h
code ends
end start
13.7 DOS 中断例程应用
;| ------------------------------- 13.7 ------------------------
;| 功能:| 屏幕第5行12列显示“Welcome to masm”
;|--------------------------------------------------------------
;| 参数:|
;|--------------------------------------------------------------
;|返回值:|
;|--------------------------------------------------------------
;| 备注:|
;|--------------------------------------------------------------
assume cs:code
data segment
db 'Welcome to masm','$'
data ends
code segment
start:
mov ah,2 ; ah存中断例程的“内部子程序编号”
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
第 14 章、端口
检测点 14.1-1
;| ------------------------------- 检测点 14.1 ------------------------
;| 功能:| 读取CMOS RAM的2号单元
;|--------------------------------------------------------------
;| 参数:|
;|--------------------------------------------------------------
;|返回值:|
;|--------------------------------------------------------------
;| 备注:|
;|--------------------------------------------------------------
assume cs:code
code segment
start:
mov al,2
out 70h,al
in al,71h
mov ax, 4c00H
int 21h
code ends
end start
检测点 14.1-2
;| ------------------------------- 检测点 14.2 ------------------------
;| 功能:| 向CMOS RAM的2号单元写入0
;|--------------------------------------------------------------
;| 参数:|
;|--------------------------------------------------------------
;|返回值:|
;|--------------------------------------------------------------
;| 备注:|
;|--------------------------------------------------------------
assume cs:code
code segment
start:
mov al,2
out 70h,al
mov al,0
out 71h,al
mov ax, 4c00H
int 21h
code ends
end start
14.3 shl 和 shr 指令
检测点 14.2
;| ------------------------------- 检测点 14.2 ------------------------
;| 功能:| 用加法和移位指令计算(ax) = (ax)*10 = (ax)*2+(ax)*8
;|--------------------------------------------------------------
;| 参数:|
;|--------------------------------------------------------------
;|返回值:|
;|--------------------------------------------------------------
;| 备注:|
;|--------------------------------------------------------------
assume cs:code
code segment
start:
mov ax,10 ;测试值10
mov bx,ax ; 暂存ax值到 bx
shl ax,1 ; (ax)=(ax) * 2
mov cl,3
shl bx,cl ;(bx)=(bx) * 8
add ax,bx ;
mov ax, 4c00H
int 21h
code ends
end start
14.4 CMOS RAM中存储的时间信息
;| ------------------------------- 检测点 14.2 ------------------------
;| 功能:|
;|--------------------------------------------------------------
;| 参数:|
;|--------------------------------------------------------------
;|返回值:|
;|--------------------------------------------------------------
;| 备注:|
;|--------------------------------------------------------------
assume cs:code
code segment
start:
mov al,8 ; CMOS RAM 8的位置存的是月
out 70h,al ; 设置要读的存储单元
in al,71h ; 将8单元的内容-月,读入al中
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
display: ; 在12行,40列处,显示月份
mov bx,0b800h
mov es,bx
mov bx,160*12+40*2
mov byte ptr es:[bx],ah
mov byte ptr es:[bx+2],al
mov ax, 4c00H
int 21h
code ends
end start
实验14
;| ------------------------------- 检测点 14.2 ------------------------
;| 功能:| 以“年/月/日 时:分:秒”的格式,显示当前的日期、时间
;|--------------------------------------------------------------
;| 参数:| dl - 存“间隔字符”
;|--------------------------------------------------------------
;|返回值:|
;|--------------------------------------------------------------
;| 备注:|
;|--------------------------------------------------------------
assume cs:code
code segment
start:
year:
mov al,9 ; CMOS RAM 9的位置存的是年
out 70h,al ; 设置要读的存储单元
in al,71h ; 将9单元的内容-月,读入al中
call calc
mov dl,'/'
mov bx,0b800h ;初始化显示的参数
mov es,bx
mov bx,160*12+40*2
call display
month:
mov al,8 ; CMOS RAM 8的位置存的是月
out 70h,al ; 设置要读的存储单元
in al,71h ; 将8单元的内容-月,读入al中
call calc
mov dl,'/'
call display
day:
mov al,7 ; CMOS RAM 7的位置存的是日
out 70h,al ; 设置要读的存储单元
in al,71h ; 将7单元的内容-月,读入al中
call calc
mov dl,0
call display
hour:
mov al,4 ; CMOS RAM 4的位置存的是时
out 70h,al ; 设置要读的存储单元
in al,71h ; 将4单元的内容-时,读入al中
call calc
mov dl,':'
call display
minutes:
mov al,2 ; CMOS RAM 2的位置存的是分
out 70h,al ; 设置要读的存储单元
in al,71h ; 将8单元的内容-分,读入al中
call calc
mov dl,':'
call display
seconds:
mov al,0 ; CMOS RAM 0的位置存的是秒
out 70h,al ; 设置要读的存储单元
in al,71h ; 将0单元的内容-秒,读入al中
call calc
mov dl,' '
call display
mov ax, 4c00H
int 21h
;-----------------------------------------------------------------------
calc:
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
ret
display:
mov byte ptr es:[bx],ah
mov byte ptr es:[bx+2],al
mov byte ptr es:[bx+4],dl
add bx,6
ret
code ends
end start
第 15 章 外中断
1、屏幕中间依次显示a~z
;| ------------------------------- 15.4 ------------------------
;| 功能:| 屏幕中间依次显示a~z
;|--------------------------------------------------------------
assume cs:code
code segment
main:;{
mov ax,0b800h
mov es,ax
mov ah,'a'
for_:;{
mov es:[160*12+40*2],ah
call delay
inc ah
cmp ah,'z'
jna for_
;}
mov ax, 4c00H
int 21h
;}
delay:;{
;// 备份后续用到的两个寄存器
push ax
push dx
;// dx存放高16位,ax存放低16位
mov dx,5h
mov ax,0
for_delay:;{
sub ax,1
sbb dx,0
cmp ax,0
jne for_delay
cmp dx,0
jne for_delay
;}
;// 恢复备份的两个寄存器的内容
pop dx
pop ax
ret
;}
code ends
end main
2、依次显示a~z,Esc 改变现实的颜色
;| ------------------------------- 15.4 ------------------------
;| 功能:| 屏幕中间依次显示a~z
;|--------------------------------------------------------------
;| 参数:| dl - 存“间隔字符”
;|--------------------------------------------------------------
;|返回值:|
;|--------------------------------------------------------------
;| 备注:
;| 1、备份BIOS `int 9`的 “中断处理程序的入口地址”
;| 2、编写自己 `int 9`的 “中断处理程序”,
;| 3、在中断向量表中设置自己 `int 9`的 “中断处理程序的入口地址”。
;| 此时发生`int 9`的中断,就可以使用自己的程序进行处理了
;| 4、依次显示a~z ,在此期间按下esc键,触发自定义的int 9 处理程序,从而改变字母的颜色属性
;| 5、恢复BIOS `int 9`的 “中断处理程序的入口地址”
assume cs:code
;// 开辟一块栈空间,给程序中的`push`指令使用
stack segment
db 128 dup (0)
stack ends
BIOS_int9_backup segment
dw 0,0
BIOS_int9_backup ends
code segment
main:;{
;设置栈相关的寄存器{
mov ax,stack
mov ss,ax
mov sp,128
;}
call backup_BIOS_int9
call set_new_int9_addr
call display_a2z
call recover_BIOS_int9_backup
mov ax, 4c00H
int 21h
;}
;// 1、备份BIOS`int 9`的 “中断处理程序的入口地址” 到
backup_BIOS_int9:;{
;// 设置“备份”内存单元的段地址
mov ax,BIOS_int9_backup
mov ds,ax
;// BIOS的int 9中断处理程序的段地址为0
mov ax,0
mov es,ax
;// 通过入栈、出栈,将int 9中断处理程序的入口地址进行备份
push es:[9*4]
pop ds:[0]
push es:[9*4+2]
pop ds:[2]
ret
;}
;// 设置自定义的“int 9中断处理程序”的入口地址
set_new_int9_addr:;{
pushf
;// 设置中断标志位为0,不允许中断以防止被别的中断打断设置入口地址指令
cli
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
popf
ret
;}
;// 依次显示a~z
display_a2z:;{
mov ax,0b800h
mov es,ax
mov ah,'a'
for_display:;{
mov es:[160*12+40*2],ah
call delay
inc ah
cmp ah,'z'
jna for_display
;}
ret
;}
;// 恢复BIOS “int 9中断处理程序”的入口地址
recover_BIOS_int9_backup:;{
mov ax,0
mov es,ax
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
ret
;}
delay:;{
;// 备份后续用到的两个寄存器
push ax
push dx
;// dx存放高16位,ax存放低16位
mov dx,5h
mov ax,0
for_delay:;{
sub ax,1
sbb dx,0
cmp ax,0
jne for_delay
cmp dx,0
jne for_delay
;}
;// 恢复备份的两个寄存器的内容
pop dx
pop ax
ret
;}
;// 自定义的`int 9`中断处理程序,esc键改变显存地址“160*12+40*2”处字符的颜色属性
int9:;{
push ax
push bx
push es
;// 将if,tf设置为0
;set0_if_tf{
;// 为什么要两个pushf?
;// 第二个`pushf` 是为了通过将标志寄存器内容出入栈来修改其内容(if,tf置零)
;// 第一个`pushf` 是为了支持BIOS中断处理程序中的`iret` 指令
;pushf
;pushf
;pop bx
;and bh,1111100b
;push bx
;popf
;}
;call dword ptr ds:[0]
;// 进入中断处理程序后,if和tf都已经设置为0了,所以上面7条指令精简后,变成2条指令
pushf
call dword ptr ds:[0]
;// 读按键的扫描码到al中
in al,60h
;if_not_esc{
; esc键改变显存地址“160*12
cmp al,1
jne int9ret
;else{
mov ax,0b800h
mov es,ax
;// 将字符的颜色属性加1,即改变目前的颜色属性
inc byte ptr es:[160*12+40*2+1]
;}
int9ret:;{
pop es
pop bx
pop ax
;}
iret
;}
code ends
end main
检测点 15.1-1
pushf
call dword ptr ds:[0]
⭐️ 有的同学会对程序中连续调用两次 pushf
有疑问,为什么要调用两次pushf
?
第二个pushf
是为了通过将标志寄存器内容出入栈来修改其内容(if,tf置零)
第一个pushf
是为了支持BIOS中断处理程序中的iret
指令
检测点 15.1-2
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
改为
pushf
;// 设置中断标志位为0,不允许中断以防止被别的中断打断设置入口地址指令
cli
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
popf
16、直接定位表
检测点16.1
;--------------------------------------------------------------------
;| ------------------------------- 15.4 ----------------------
;| 功能:| 将code段中a处的8个数据累加,结果存储到b处的双字中
assume cs:code
code segment
a dw 1,2,3,4,5,6,7,8
b dd 0
start:;{
mov si,0
mov cx,8
for_:;{
mov ax,a[si]
add a[si+2],ax
adc b[0],0
add si,2
loop for_
;}
mov ax,4c00h
int 21h
;}
code ends
end start
16.2 其他段中使用数据标号
;| ------------------------------- 16.2 ----------------------
;| 功能:| 将data段中a处的8个数据累加,结果存储到b处的字中
;|-------------------------------------------------------------
assume cs:code ,ds:data
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
data ends
code segment
main:;{
mov ax,data
mov ds,ax
mov si,0
mov cx,8
for_:;{
mov b,2
mov al,a[si]
mov ah,0
add b,ax
inc si
loop for_
;}
mov ax,4c00h
int 21h
;}
code ends
end main
检测点 16.2
assume cs:code ,es:data
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
data ends
code segment
main:;{
mov ax,data
; // data 段与es建立的联系
mov es,ax
mov si,0
mov cx,8
for_:;{
mov al,a[si]
mov ah,0
add b,ax
inc si
loop for_
;}
mov ax,4c00h
int 21h
;}
code ends
end main
16.3 直接定址表
程序1
现在要取al中高4位和低4位的内容,然后在屏幕上用16进制显示出来(需要对内容进行数值转换,直接显示会显示数值对应的ASCII码字符)
我们想得到的结果如下图,然后将ah,al的内容作为偏移量到’字符表’中取对应的字符,然后发送到显存进行显示
assume cs:code
code segment
main:;{
jmp short show
table db '0123456789ABCDEF'
show:;{
push bx
push es
;// 获取al中高4位的值,放入ah中,用做table的偏移量
mov ah,al
mov cl,4
shr ah,cl
;// 获取al中低4位的值,用作table的偏移量
and al,0001111b
;//从talbe中取al高4位的16进制字符
mov bl,ah
mov bh,0
mov ah,table[bx]
;// 显示字符
mov bx,0b800h
mov es,bx
mov es:[160*12+40*2],ah
;//从talbe中取al低4位的16进制字符
mov bl,al
mov bh,0
mov al,table[bx]
mov es:[160*12+40*2+2],al
pop es
pop bx
ret
;}
mov ax,4c00h
int 21h
;}
code ends
end main
程序2
assume cs:code
code segment
main:;{
jmp short show
table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180
ag0 db '0',0
ag30 db '0.5',0
ag60 db '0.866',0
ag90 db '1',0
ag120 db '0.866',0
ag150 db '0.5',0
ag180 db '0',0
show:;{
push bx
push es
push si
mov bx,0b800h
mov es,bx
;// (角度值/30)作为table的偏移量,取得对应字符串的偏移地址放入bx中
mov ax,60
mov bl,30
div bl
mov bl,al
mov bh,0
add bx,bx
mov bx,table[bx]
mov si,160*12+40*2
for_:;{
mov ah,cs:[bx]
cmp ah,0;// 取到的值是0则跳出循环
je show_return
mov es:[si],ah
inc bx
add si,2
jmp short for_
;}
show_return:
pop si
pop es
pop bx
; // ret 不应该有,否则程序又返回for_循环中了
;ret
;}
mov ax,4c00h
int 21h
;}
code ends
end main
程序2 改进
assume cs:code
code segment
table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180
ag0 db '0',0
ag30 db '0.5',0
ag60 db '0.866',0
ag90 db '1',0
ag120 db '0.866',0
ag150 db '0.5',0
ag180 db '0',0
main:;{
back:;{
push bx
push es
push si
;}
;/*
; * (角度值/30)作为table的偏移量,然后取得对应字符串的偏移地址放入bx中
; * 参数: ax存放角度;
; * 返回值: bx存放返回值-sin x 结果字符串的偏移地址;
; */
mov ax,30
get_value_offset:;{
mov bl,30
div bl
mov bl,al
mov bh,0
;// table中的偏移地址占两个字节,所以bx要乘以2
add bx,bx
;// 获取角度对应的`sin x` 值的字符串偏移量
mov bx,table[bx]
;}
;// 显示sin x 的值
display:;{
mov ax,0b800h
mov es,ax
mov si,160*12+40*2
for_:;{
mov ah,cs:[bx]
cmp ah,0
;// 取到的值是0则跳出循环,结束程序
je main_end
mov es:[si],ah
inc bx
add si,2
jmp short for_
;}
;}
main_end:;{
pop si
pop es
pop bx
; // ret 不应该有,否则程序又返回for_循环中了
;ret
mov ax,4c00h
int 21h
;}
;}
code ends
end main
16.4
assume cs:code,ds:stack
stack segment
dw 0;
stack ends
code segment
;table dw clear_screem,set_screem_front_color,set_screem_bg_color,roll_next
main:;{
mov ah,3
;push bx
;cmp ah,3
;ja ret_
;mov bl,ah
;mov bh,0
;add bx,bx
;call word ptr table[bx]
;ret_:
; pop bx
; ret
cmp ah,0
je do1
cmp ah,1
je do2
cmp ah,2
je do3
cmp ah,3
je do4
end_:;{
mov ax,4c00h
int 21h
;}
do1:;{
call clear_screem
jmp short end_
;}
do2:;{
call set_screem_front_color
jmp short end_
;}
do3:;{
call set_screem_bg_color
jmp short end_
;}
do4:;{
call roll_next
jmp short end_
;}
;}
;/*
; * 1、清屏:将显存中当前屏幕中的字符设置为空格符
; */
clear_screem:;{
call backup_reg
mov bx,0b800h
mov es,bx
mov bx,0
;// 80 *25 = 2000,一屏显示的字符个数
mov cx,2000
for_clear_screem:;{
mov byte ptr es:[bx],' '
add bx,2
loop for_clear_screem
;}
call recover_backup_reg
ret
;}
;/*
; * 2、设置前景颜色,设置显存中当前屏幕中处于奇数地址的属性字节的第0,1,2位
; */
set_screem_front_color:;{
call backup_reg
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
for_set_screem_front_color:;{
and byte ptr es:[bx],11111000b
or es:[bx],al
add bx,2
loop for_set_screem_front_color
;}
call recover_backup_reg
ret
;}
;/*
; * 3、设置背景颜色:设置显存中当前屏幕中处于奇数地址的属性字节的第4,5,6位
; */
set_screem_bg_color:;{
call backup_reg
mov cl,4
shl al,cl
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
for_set_screem_bg_color:;{
and byte ptr es:[bx],10001111b
or es:[bx],al
add bx,2
loop for_set_screem_bg_color
;}
call recover_backup_reg
ret
;}
;/*
; * 4、向上滚动一行: 依次将第n+1行的内容复制到第n行处,最后一行为空
; */
roll_next:;{
call backup_reg
mov si,0b800h
mov es,si
mov ds,si
mov si,160
mov di,0
cld
mov cx,24
for_roll_next:;{
push cx
mov cx,160
rep movsb
pop cx
loop for_roll_next
;}
;// 设置屏幕的最后一行(80列)为空格字符
mov cx,80
mov si,0
for_clear_last_row:;{
mov byte ptr [160*24+si],' '
add si,2
loop for_clear_last_row
;}
call recover_backup_reg
ret
;}
;/*
; * 备份几个后面要用到的寄存器
; */
backup_reg:;{
;// 调用者的ip出栈,放入ax中,ret调用前再压栈,
pop ax
push bx
push cx
push si
push di
push es
push ds
;//将调用者ip的值入栈,功能ret用
push ax
ret
;}
;/*
; * 恢复备份的寄存器数据
; */
recover_backup_reg:;{
;// 调用者的ip出栈,放入ax中,ret调用前再压栈,
pop ax
pop ds
pop es
pop di
pop si
pop cx
pop bx
;//将调用者ip的值入栈,功能ret用
push ax
ret
;}
code ends
end main
17、使用BIOS进行键盘输入和磁盘读写
17.2、根据输入的字符改变屏幕中字体颜色
;| ------------------------------- 15.4 ------------------------
;| 功能:| 键盘输入字符来改变屏幕上所有字符的颜色,r 为红色,b为蓝色,g为绿色
;|--------------------------------------------------------------
assume cs:code
code segment
main:;{
;// 执行int 16h的0号功能
mov ah,0
int 16h
;// ah保存颜色属性,1表示前景色为蓝
mov ah,1
cmp al,'r'
je red
cmp al,'g'
je green
cmp al,'b'
je blue
jmp short main_end
red:
;// ah保存颜色属性,从这里开始,会左移2位,变成4,表示红色
shl ah,1
green:
;// ah保存颜色属性,从这里开始,会左移1位,变成2,表示绿色
shl ah,1
blue:
mov bx,0b800h
mov es,bx
;// 屏幕中字符颜色属性的起始偏移地址为1
mov bx,1
mov cx,2000
for_:;{
;// 颜色属性的后三位是前景色
and byte ptr es:[bx],11111000b
;// 设置字符前景色
or es:[bx],ah
add bx,2
loop for_
;}
jmp main
main_end:;{
mov ax, 4c00H
int 21h
;}
;}
code ends
end main
17.3、字符串输入
复习:int 16
读键盘缓冲区,结果放在ax中,al是ASCII码,ah是扫描码
;| ------------------------------- 17.3 字符串输入 ------------------------
;| 功能:| 键盘输入字符来改变屏幕上所有字符的颜色,r 为红色,b为蓝色,g为绿色
;|--------------------------------------------------------------
assume cs:code
code segment
;----------- 接收字符串输入的子程序 -----------------------------
getstr:
push ax
;// 循环获取字符
for_getstrs:;{
mov ah,0
int 16h
;if(输入的不是字符){
;// ascii码小于20h,说明不是字符
cmp al,20h
jb notchar
;}
;// ah开始用来存储功能号,0 入栈,1 出栈,2显示
;pushChar{
mov ah,0
call charstack
;}
;displayChar{
mov ah,2
call charstack
;}
jmp for_getstrs
;}
;//
notchar:;{
;// 0eh为退格键的扫描码
cmp ah,0eh
je backspace
;// 1ch为回车键的扫描码
cmp ah,1ch
je enter
jmp for_getstrs
;}
backspace:;{
mov ah,1
call charstack
mov ah,2
call charstack
jmp for_getstrs
;}
enter:;{
mov al,0
mov ah,0
call charstack
mov ah,2
call charstack
;}
pop ax
ret
;----------- 字符栈的入栈、出栈和显示 -----------------------------
charstack:
jmp short charstart
table dw charpush,charpop,charshow
top dw 0
charstart:;{
push bx
push dx
push di
push es
;if(功能号大于2){
cmp ah,2
; 返回
ja sret
;}
;根据ah的值来选择功能执行{
mov bl,ah
mov bh,0
add bx,bx
;// 根据ah的值从table中获取偏移地址
jmp word ptr table[bx]
;}
;}
charpush:;{
mov bx,top
mov [si][bx],al
inc top
jmp sret
;}
charpop:;{
cmp top,0
je sret
dec top
mov bx,top
mov al,[si][bx]
jmp sret
;}
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
jne noempty
mov byte ptr es:[di],' '
jmp sret
;}
noempty:;{
mov al,[si][bx]
mov es:[di],al
mov byte ptr es:[di+2],' '
inc bx
add di,2
jmp charshows
;}
;}
;// 恢复备份,并ret
sret:;{
pop es
pop di
pop dx
pop bx
ret
;}
code ends
end getstr
附录A: 通用程序
1、拷贝代码的程序
2、延迟
delay:;{
;// 备份后续用到的两个寄存器
push ax
push dx
;// dx存放高16位,ax存放低16位
mov dx,5h
mov ax,0
for_delay:;{
sub ax,1
sbb dx,0
cmp ax,0
jne for_delay
cmp dx,0
jne for_delay
;}
;// 恢复备份的两个寄存器的内容
pop dx
pop ax
ret
;}
附录B:颜色属性
内存空间中,0xB8000H ~ 0xBFFFFH,共32KB空间为显存空间。
将32KB的空间分布为8页,每页4KB,显示器可以显示任一页的内容,但一般情况下,显示第0也的内容。
屏幕上的每个字符对应显存中“两个连续的字节”,第一个是字符的ASCII码,后一个是字符的“显示属性”。显示属性格式如下: