8086汇编#12

第十二章至第十七章 – 汇编语言提纲

第十二章 内中断 / 第十三章 int指令 / 第十四章 端口 / 第十五章 外中断 / 第十六章 直接定址表 / 第十七章 使用BIOS进行键盘输入和磁盘读写


第十二章 内中断

第十三章 int指令

第十四章 端口

第十五章 外中断

第十六章 直接定址表

第十七章 使用BIOS进行键盘输入和磁盘读写

一、直接定址表

1. 移位指令(14.3)

(1) 逻辑左移 shl opr,cnt

功能:

(i)将一个寄存器或内存单元中的数据向左移位

(ii)将最后移出的一位写入CF中

(iii)最低位用0填充

(2)逻辑右移 shr opr,cnt

与shl所执行的操作恰好相反,但是最高位用0填充

(3)循环左移 rol opr,cnt

将移出来的位放在低位,不会补0,其他与逻辑左移相似

(4) 循环右移 ror opr,cnt

与rol相反

(5) 算术左移 sal opr,cnt

与逻辑左移一致

(6) 算术右移 sar opr,cnt

与逻辑右移相似,但是符号位保持不变

(7) 带进位循环左移 rcl opr,cnt

最高位放入CF,最低位将原有CF的值移入

(8) 带进位循环右移 rcr opr,cnt

与rcl相反

*s/sh=shift,l=left,r=right,a=arithmetic,r/ro=rotate,c=carry

将X逻辑左移1位相当于执行X=X*2,右移1位,相当于执行X=X/2

当移动位数大于1时,必须将移动位数放在cl中

检测点14.2

编程,用加法和移位指令计算(ax)=(ax)*10

提示:(ax)*10=(ax)*2+(ax)*8

assume cs:code 
code segment 
start:  mov bx,ax
        shl ax,1;左移1位(ax)=(ax)*2
        mov cl,3
        shl bx,cl       ;左移3位(bx)=(ax)*8
        add ax,bx       ;(ax)=(ax)*2+(ax)*8
        mov ax,4c00h 
        int 21h 
code ends 

end start

2. 描述了单元长度的标号(16.1~16.2):格式:

标号 db/dw/dd 数据

在标号处定义数据,标号不需要冒号,这些标号称作数据标号,指名了数据的单元和长度。

assume cs:code

code segment

a db 1,2,3,4,5,6,7,8
b dw 0

……

这些标号(如上面的a,b)不但表示内存地址,还表示了内存单元的长度。标号a描述了地址code:0,并且从这个地址开始,之后的内存单元都是字单元。

使用时作为一个地址使用,可以当作变量(在编译时转为用[…]表示的形式),在指令中代表一个段中的内存单元。

地址标号仅仅只能表示一个地址,且只能在代码段中使用

若要在其他段中使用地址标号,在代码段中直接用数据标号访问数据,需要用assume将标号所在段与某一个段寄存器关联起来,否则编译器无法确定标号的段地址所在段寄存器。

可以将标号当作数据定义,此时编译器将标号所表示的地址当作数据的值

a db 1,2,3,4,5,6,7,8
b dw 0
c dw a,b
d dd a,b

此时c中存储了a和b的偏移地址

d中存储了a和b的段地址和偏移地址,相当于 d dw offset a,seg a,offset b,seg b

seg操作符用于取段地址

检测点16.1

下面的程序将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
s:mov ax,a[si]
add a[16],ax

adc a[18],0

add si,2
loop s
mov ax,4c00h
int 21h

code ends

end start

检测点16.2

下面的程序将data段中a处的8个数值累加,结果存储到b处的双字节中,补全程序。

assume cs:code,es:data

data segment

a db 1,2,3,4,5,6,7,8
b dw 0

data ends

code segment

start: mov ax,data   
mov es,ax       
mov si,0
mov cx,8
s:mov al,a[si]
mov ah,0
add b,ax
inc si
loop s

mov ax,4c00h
int 21h

code ends

end start

3. 直接定址表(16.3):查表解决问题

*定义:可以通过依据数据,直接计算出所要查找的元素的位置的表叫做直接定址表

*情境:显示给定字节型数据的十六进制形式

数值0~9与数字字符’0’~’9’的对应关系

数值+30H=对应字符的ASCII值

数值10~15与字符’A’~’F’之间的对应关系

数值+37H=对应字符的ASCII值

*方案:建立字符表,存放’0’~’F’,可以通过数值找到对应的字符

如:table db '0123456789ABCDEF'

将ah中的数字显示出来:

mov bl,ah
mov bh,0
mov ah,table[bx]

*总结:当两个数据集合之间可以建立对应的映射时,可以进行编排,形成一个表,可以用查表的方法根据给出的数据得到其在另一个集合中的对应数据

*分类:代码的直接定址表、数据的直接定址表

*代码的直接定址表:可以存储子程序中多个功能号的地址,需要使用时计算一下表中元素的位置,然后找到其中存储的功能的地址,然后跳转

如: dw func1,func2,func3,func4

可以根据功能号1\2\3\4来找到对应的地址,需要扩充时只需要添加表项

二、内中断(Chapter 12&13)

1. 中断

中断信息是CPU从内部或外部检测到发来的特殊信息,此时CPU不再接着执行下面的指令,而是去处理中断信息

外中断:外部设备发生的事件引起中断

内中断:CPU内部发生的事件引起的中断

2. 内中断的产生:

(1) 除法错误,如执行div产生溢出

(2) 单步执行

(3) 执行into指令

(4) 执行int指令

3. CPU处理中断需要中断类型码,中断类型码用于标识中断信息来源。中断类型码为一字节,可以表示256种中断信息

4. 中断信息的来源叫做中断源

5. 4种中断的中断类型码:

(1) 除法错误:0 (2)单步执行:1 (3)执行into指令:4

(4)执行int指令:格式int n 用于调用n号中断,n为字节型立即数

6.用于处理中断信息的程序叫做中断处理程序,CPU在收到中断信息后会执行中断处理程序

7.中断向量表

中断向量表是中断向量的列表,中断向量就是中断处理程序入口地址。

知道中断类型码就可以定位中断向量表表项,8086CPU的中断向量表存放在内存地址0处,范围:0000:0000~0000:03ff共1024字节。一个表项存放一个中断向量,占用两个字,高字存放段地址,低字存放偏移地址

检测点12.1

(1)用debug查看内存,情况如下:

0000:0000 68 10 A7 00 8B 01 70 00-16 00 9D 03 8B 01 70 00

则3号中断源对应的中断处理程序入口的偏移地址的内存单位的地址为: 0070:018b

(2)存储N号中断源对应的中断处理程序入口的偏移地址的内存单元的地址为: 4N

存储N号中断源对应的中断处理程序入口的段地址的内存单元的地址为: 4N+2

8. 中断过程

·由CPU自动完成,先找到中断向量,设置CS和IP。该过程叫做中断过程

(1) 取得中断类型码n

(2) 标志寄存器的值入栈(中断过程可能改变标志寄存器的值)pushf

(3) 设置标志寄存器的第8位TF和第9位IF的值为0TF=0,IF=0

(4) CS入栈push CS

(5) IP入栈push IP

(6) 设置中断处理程序的入口地址(IP)=(N*4),(CS)=(N*4+2)

在执行完中断处理程序后,需要恢复在进入中断处理程序之前的CPU现场,因此需要在修改标志寄存器前将他们的值保存入栈

9. 编制中断处理程序

·中断处理程序要常驻内存,因此要选择一个位置

解决方法:[简便]找到一块其他程序不会用的内存,如0000:0200~0000:02ff没有被使用

注意:在工程上不要这样做,要去其他安全的内存中存放(除非自己编写操作系统)

·要在中断向量表中登记

·中断处理程序的编写方法:

(1) 保存用到的寄存器

(2) 处理中断

(3) 恢复用到的寄存器

(4) 用IRET指令返回

iret指令的功能:

pop IP
pop CS

popf

iret通常用于中断过程结束后的返回,用于恢复CS和IP以及标志寄存器,以便于回到原来的位置。

·处理0号中断

可以编写一个do0的安装程序,前面存储了安装程序,后面存储do0,执行后将do0安装到内存

(1)安装的技巧:可以用标号确定程序的长短,利用编译器计算do0的长短

mov cx,offset do0end-offset do0

…

do0:

…

do0end:nop

编译器可以将一些表达式在编译时计算,如0*4+2

+ – * \都是编译器识别的运算符号,编译器可以用它们进行常数的减法

如mov ax,(5+3)*5/10等价于 mov ax,4

(2) 在中断处理程序中使用的常量和变量应存储在处理程序中,不能在安装程序开辟一个数据段存储。在中断处理程序中可以用一段区域存储数据,在开始时用jmp指令跳到程序真正的开始位置,如:

do0:jmp short do0start
db "overflow!"
do0start:mov ax,cs
mov ds,ax
mov si,202h
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

10. 单步中断(debug的t命令使用)

(1) TF(陷阱标志,trap flag):用于调试时的单步方式操作。当TF=1时,每条指令执行完毕后产生陷阱,由系统控制计算机,当TF=0时,CPU正常工作,不产生陷阱

(2) IF(中断标志,interrupt flag)当IF=1时,允许CPU相应可屏蔽中断请求,当IF=0时,关闭中断,不响应。

(3) 单步中断的过程:

①取得中断类型码1

②标志寄存器入栈,TF和IF设为0

③CS、IP入栈

④(IP)=(1*4),(CS)=(1*4+2)

若TF=1,则在执行单步中断时又再次引发单步中断,便会循环下去.因此在处理单步中断时要把TF设为0

11. 中断不响应

在执行完向SS寄存器传送数据的指令后,CPU不响应中断

原因:SS:SP指向栈顶,对他们的设置要连续完成。要保证栈操作的正确

所以设置SP的指令应紧跟在设置SS的指令之后

12.int指令

int指令的格式:int n

相当于引发一个n号中断的过程:

(1) 取中断类型码n

(2) 标志寄存器入栈,IF=0,TF=0

(3) CS、IP入栈

(4) (IP)=(n*4),(CS)=(n*4+2)

系统会将一些子程序以中断处理程序(中断例程)的形式提供。

12. 编写供程序调用的中断例程

(1) 保存要用到的寄存器

(2) 处理中断

(3) 恢复寄存器

(4) 用iret返回

检测点13.1

7ch中断例程如下:

lp:push bp
mov bp,sp
dec cx

jcxz lpret

add [bp+2],bx
lpret:pop bp 

iret

bx存放位移

(1)在上面的内容中,我们用7ch中断例程实现loop的功能,则上面的7ch中断例程所能进行的最大转移位移是多少?

答:最大位移是FFFFH

(2)用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,12*160
s:cmp byte ptr [si],0
je ok
mov al,[si]
mov es:[di],al
inc si
add di,2
mov bx,offset s-offset ok
int 7ch
ok:mov ax,4c00h
int 21h

code ends

end start

jmp near ptr s指令的功能为:(ip)=(ip)+16位移,实现段内近转移,答案:
push bp
mov bp,sp
add [bp+2],bx;ok的偏移地址+bx得到s的偏移地址
pop bp

iret

13. bios和dos中断例程

bios容量:8KB,地址:FE000H开始

bios中的内容:

(1) 硬件系统的检测初始化程序

(2) 外部中断和内部中断的中断例程

(3) 用于对硬件设备进行I/O操作的中断例程

(4) 其他和硬件系统相关的中断例程

bios和dos中断的安装:

(1) 开机后,CPU加电初始化,(cs)=0FFFFH,(ip)=0,在FFFF:0处有一条跳转指令,执行后CPU进入BOIS中的硬件系统的检测初始化程序

(2) 建立中断向量

(3) 完成硬件系统检测和初始化后,调用int 19h进行操作系统引导,计算机由操作系统控制

(4) dos启动并建立中断向量

检测点13.2

判断下面说法的正误:

(1)我们可以编程改变FFFF:0处的指令,使得CPU不去执行BIOS中的硬件系统检测和初始化程序。

答:错误,FFFF:0处的内容无法改变。

(2)int 19h中断例程,可以由DOS提供。

答:错误,先调用int 19h,后启动DOS。

BIOS的10h中断:ah=功能号

·(ah)=2设置光标

bh=页号

dh=行号(0~24)

dl=列号(0~79)

·(ah)=9在光标位置显示字符

al=字符

bl=颜色属性(与显存属性字节格式相同)

bh=页号

cx=字符重复个数

DOS的21h中断:ah=功能号

·(ah)=4ch程序返回

al=返回结果

·(ah)=09H在光标位置显示字符串

ds:bx=字符串地址(以$结尾)

三、端口和外设(14.0~14.2、14.4、Chapter 15&17)

1. 端口及其使用(14.0~14.1)

(1) 在主板上的各类芯片(接口卡、接口芯片等)中,都有一组可以供CPU读写的寄存器,它们

①都与CPU的总线相连

②CPU对他们进行读或写的操作时都通过控制线向它们所在的芯片发出端口读写命令

CPU会对这些寄存器进行统一编制,作为端口,建立一个端口地址空间。每个端口在端口地址空间都有一个地址。(端口地址空间有64K,即端口号的范围为0~65535)

(2) 端口的读写:只有两条指令:in out

端口地址通过地址总线传送

·端口的访问

in al,60h

执行时相关操作:

(i)CPU通过地址线将地址信息60H发出

(ii)CPU通过控制线翻出端口读命令,选中端口所在芯片,并通知要从中取出数据

(iii)端口所在芯片将60h端口中的数据通过数据线送入CPU

*端口分配:

对于0~255以内的端口进行读写,端口号用立即数给出

对于256~65535的端口号,要在dx中存放

in和out指令只能使用ax或al来存放从端口读入的数据或要发送到端口的数据。访问8位端口用al,访问16位端口用ax.格式如下:

in al/ax,端口号

out 端口号,al/ax

端口号在0~255时

in al/ax,idata

out idata,al/ax

端口号在256~65535时

in al/ax,dx

out dx,al/ax

2. CMOS RAM芯片(14.2、14.4)

(1) 包含一个实时钟和一个有128个存储单元的RAM存储器(早期为64个)

(2) 该芯片靠电池驱动。关机后内部时钟仍然可以工作,RAM信息不丢失

(3) 内部实时钟占用内部的RAM的0~0dh单元保存时间信息,其余单元大部分用于保存系统配置信息,供启动时BIOS读取

(4) 内部有两个端口70h和71h

(5) 70h为地址端口,存放要访问的CMOS RAM单元的地址,71h为数据端口,存放从选定的CMOS RAM中读取的数据,或要向其中写入的数据。

如:读CMOS RAM 2号单元:

①将2送入端口70h

②从端口71h读出2号单元的内容

·提取当前时间

当前时间在CMOS RAM中用6个字节存放(6个信息长度都是一字节)

如5月15日=> 1 5 0 5 =0010 0101 0000 0101

使用的是BCD码存放

检测点 14.1

(1)编程,读取CMOS RAM的2号单元内容。

assume cs:code 
code segment 
start:  mov al,2        ;赋值al 

out 70h,al ;将al送入端口70h

in al,71h ;从端口71h处读出单元内容

        mov ax,4c00h 
        int 21h 
code ends 

end start

(2)编程,向CMOS RAM的2号单元写入0。

assume cs:code 
code segment 
start:  mov al,2        ;赋值al 

out 70h,al ;将al送入端口70h

        mov al,0        ;赋值al 

out 71h,al ;向端口71h写入数据al

        mov ax,4c00h 
        int 21h 
code ends 

end start

3. 外中断(Chapter15)

(1) 外中断源的分类

①可屏蔽中断:CPU可以不响应的外中断。当IF=1时,CPU响应可屏蔽中断,当IF=0时则不响应可屏蔽中断

可屏蔽中断的中断类型码通过数据总线送入

在中断过程中将IF置为0是为了防止被其他的可屏蔽中断干扰

若要在中断处理程序中处理可屏蔽中断,可以将IF置为1

设置IF的指令:sti 设置IF=1;cli设置IF=0

②不可屏蔽中断:CPU必须响应的中断。当CPU检测到不可屏蔽中断时,在执行完当前指令后立即响应。

不可屏蔽中断的中断类型码为2

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

(2) PC键盘的处理过程

·键盘的每个键相当于一个开关,键盘中有一个芯片对键盘上每一个键的开关状态进行扫描。

·按下键时,开关接通,该芯片产生一个扫描码,扫描码说明了键盘上所按键的位置,扫描码被送入芯片的端口60h中,松开按键时也产生一个扫描码,此时扫描码也被送入60h中

·按下时产生的扫描码叫做通码,松开时的扫描码叫做断码

断码=通码+80h

·键盘输入到达60h时,相关芯片向CPU发送9H中断,该中断可以屏蔽。

·BIOS提供int 9中断例程,用来进行基本的键盘输入处理,工作流程如下:

①读出60h端口中的扫描码

②若是字符键的扫描码,将扫描码和它所对应的字符码(ASCII码)送入内存中的BIOS键盘缓冲区,若是控制键,将其转换为状态字节写入内存中存储状态字节的单元0040:17

③对键盘系统进行控制

BIOS的键盘缓冲区可以存储15个键盘输入,一个键盘输入占了1个字,高字节放扫描码,低字节放字符码

键盘状态字节各位记录的信息:

位

0

1

2

3

4

5

6

7

代表的键

右shift

左shift

ctrl

alt

ScrollLock

NumLock

CapsLock

Insert

说明

Scroll

数字键

大写锁

删除键

置为1表示按下这些键设置了状态

(3) 定制键盘输入

延时例程的设计:

让CPU执行空循环,循环的次数必须快。此处用两个16位寄存器存放32位的循环次数

mov dx,1000h
mov ax,0
s:sub ax,1

sbb dx,0

cmp ax,0
jne s
cmp dx,0
jne s

拓展例程:在新的int 9调用原有的int 9

此处采取模拟int指令的策略:

pushf

pop ax
and ah,11111100b ;IF和TF为标志寄存器的第9位和第8位
push ax

popf

call dword ptr ...;CS、IP入栈

检测点15.1

(1) 仔细分析一下书中的in9中断例程,看看是否可以精简一下?

其实在我们的int 9中断例程中,模拟int指令调用原int 9中断例程的程序段是可以精简的,因为在进入中断例程后,IF和TF都已置0,没有必要再进行设置了,对于程序段:

pushf;标志寄存器入栈

pushf

pop bx
and bh,11111100b;IF和TF为flag的第9位和第8位
push bx

popf;TF=0,IF=0

call dword ptr ds:[0];CS、IP入栈;(IP)=ds:[0],(CS)=ds:[2]

可以精简为:

pushf;标志寄存器入栈

call dword ptr ds:[0];CS、IP入栈;(IP)=ds:[0],(CS)=ds:[2]

两条指令。

(2) 仔细分析程序中的主程序,看看有什么潜在的问题?

在主程序中,如果在设置执行设置int 9中断例程的段地址和偏移地址的指令之间发生了键盘中段,则CPU将转去一个错误的地址执行,将发生错误。

找出这样的程序段,改写他们,排除潜在的问题。

;在中断向量表中设置新的int 9中断例程的入口地址

cli;设置IF=0屏蔽中断

mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs

sti;设置IF=1不屏蔽中断

============更改后的int 9中断例程================

;功能:在屏幕中间依次显示’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

;将原来的int 9中断例程的入口地址保存在ds:0、ds:2单元中

mov ax,data
mov ds,ax

mov ax,0
mov es,ax

push es:[9*4]
pop ds:[0]
push es:[9*4+2]
pop ds:[2]

;在中断向量表中设置新的int 9中断例程的入口地址

cli;设置IF=0屏蔽中断

mov word ptr es:[9*4],offset int9
mov word ptr es:[9*4+2],cs

sti;设置IF=1不屏蔽中断

;依次显示’a’~’z’

mov ax,0b800h
mov es,ax
mov ah,'a'
s:mov es:[160*12+40*2],ah;第12行第40列
inc ah
cmp ah,'z'

jnb s

;将中断向量表中int 9中断例程的入口恢复为原来的地址

mov ax,0
mov es,ax

push ds:[0]
pop ss:[9*4]
push ds:[2]
pop es:[9*4+2]

;结束

mov ax,4c00h
int 21h

;循环延时,循环100000h次

delay:push ax
push dx
mov dx,1000h
mov ax,0
delay1:sub ax,1

sbb dx,0;(dx)=(dx)-0-CF

cmp ax,0
jne delay1
cmp dx,0
jne delay1
pop dx
pop ax

ret

;以下为新的int 9中断例程

int9:push ax
push bx
push es

in al,60h;从端口60h读出键盘输入

;对int指令进行模拟,调用原来的int 9中断例程

pushf;标志寄存器入栈

call dword ptr ds:[0];CS、IP入栈;(IP)=ds:[0],(CS)=ds:[2]

;如果是ESC扫描码,改变显示颜色

cmp al,1;和esc的扫描码01比较
jne int9ret;不等于esc时转移

mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1];将属性值+1,改变颜色

int9ret:pop es
pop bx
pop ax

iret

code ends

end start

4. 使用BIOS进行键盘输入和磁盘读写(Chapter17)

(1) int9中断例程对键盘输入的处理

键盘缓冲区中有16个字单元,可以存储15个按键的扫描码和对应的ASCII码

(2) 使用int 16h中断例程读取键盘缓冲区

参数:(ah)=功能号

功能号0:从键盘缓冲区中读取一个键盘输入,并将其从键盘缓冲区中删除

返回:(ah)=扫描码,(al)=ASCII码

若发现缓冲区空,则循环等待,直到缓冲区中有数据

检测点17.1

“在int 16h中断例程中,一定有设置IF=1的指令。”这种说法对吗?

正确,当键盘缓冲区为空时,如果设置IF=0,int 9中断无法执行,循环等待会死锁。

(3)字符串的输入

最基本的字符串输入程序的功能:

①.输入时显示字符串

②.在输入回车符时输入结束

③.能删除已经输入的字符

设计程序:

①调用int 16h读取输入

②字符就进入字符栈,显示字符栈中的所有字符,继续执行①

③退格键按下后从字符栈中弹出一个字符,显示字符栈中的所有字符,继续执行①

④若是Enter,向字符栈中压入0,返回

(4)应用int 13h读写磁盘

入口参数

(ah)=功能号

2表示读,3表示写

(al)=操作的扇区数

(ch)=磁道号

(cl)=扇区号

(dh)=磁头号(对于软盘是面)

(dl)=驱动器号

软驱从0开始,0(A:),1(B:)

硬盘从80h开始80h(C:),81h(D:)

es:bx指向接收从扇区读入数据的内存区(或将写入磁盘的数据)

返回参数:

操作成功:(ah)=0,(al)=读入(写入)扇区数

操作失败:(ah)=出错代码

磁盘的实际访问由磁盘控制器进行,可以通过控制磁盘控制器访问磁盘。只能以扇区为单位读写磁盘。读写扇区要给出面(磁头)号、磁道号和扇区号。面号和磁道号从0开始,扇区号从1开始。

四、课程实验

实验12 编写0号中断处理程序

编写0号中断处理程序,使得在除法溢出发生时,在屏幕中间显示字符串“divide error!”,然后返回DOS

Program ex-12-1自主解法

assume cs:code
code segment
start:
;设置相关寄存器传送代码
mov ax,cs ;do0所在源位置
mov ds,ax
mov si,offset do0
mov ax,0
mov es,ax
mov di,200h
mov cx,offset do0end-offset do0
;(cx)=do0长度
cld
rep movsb
;设置中断向量
mov ax,0
mov ds,ax
mov word ptr ds:[2],0h
mov word ptr ds:[0],200h
mov ax,4c00h
int 21h
;--------------------------------
;以下代码送入0:200
do0:
jmp do0start
string db 'divide error!' ;13Byte
do0start:
mov ax,0b800h
mov es,ax
mov di,(40*25+12)*2
mov ax,0h
mov ds,ax
mov si,203h
;string的位置,等价于200h+(offset string-offset do0)
mov cx,13
loop1:
mov ah,2
mov al,ds:[si]
inc si
mov es:[di],ax
add di,2
loop loop1
mov ax,4c00h
int 21h
do0end:nop
code ends
end start

Program ex-12-2 参考解法

assume cs:code 

code segment

start:  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 ;设置传输方向为正

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 'divide error!'

do0start:mov ax,cs
         mov ds,ax
         mov si,202h                    ;设置ds:si指向字符串

         mov ax,0b800h
         mov es,ax
         mov di,12*160+34*2             ;设置es:di指向显存空间的中间位置

         mov cx,13                      ;设置cx为字符串长度
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

实验13 编写、应用中断例程

(1)编写并安装int 7ch中断例程,功能为显示一个用0结束的字符串,中断例程安装在0:200处。

效果图

Program ex-13-1 参考解法

;名称:int 7ch中断例程

;功能:显示一个0结束的字符串,中断例程安装在0:200处

;参数:(dh)=行号,(dl)=列号,(cl)=颜色,ds:si指向字符串首地址

assume cs:code   
code segment  
start:  mov ax,cs  
        mov ds,ax  
        mov si,offset show_str          ;设置ds:si指向源地址  
        mov ax,0  
        mov es,ax  
        mov di,200h                     ;设置es:di指向目标地址  
        mov cx,offset show_strend-offset show_str ;设置cx为传输长度  

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  

;名称:show_str

;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。

;参数:(dh)=行号(取值范围0~24);

; (dl)=列号(取值范围0~79);

; (cl)=颜色;

; ds:si指向字符串的首地址。

;返回:无。

show_str:push ax 
        push bx 
        push es 
        push si 
        mov ax,0b800h 
        mov es,ax 
        mov ax,160 
        mul dh 
        mov bx,ax        ;bx=160*dh 
        mov ax,2 
        mul dl           ;ax=dl*2 
        add bx,ax        ;mov bx,(160*dh+dl*2)设置es:bx指向显存首地址 
        mov al,cl        ;把颜色cl赋值al 
        mov cl,0 
show0:  mov ch,[si] 

jcxz show1 ;(ds:si)=0时,转到show1执行

        mov es:[bx],ch 
        mov es:[bx].1,al 
        inc si           ;ds:si指向下一个字符地址 
        add bx,2         ;es:bx指向下一个显存地址 
        jmp show0 
show1:  pop si 
        pop es 
        pop bx 
        pop ax 

iret

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

end start

;实验13(1)应用举例

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

(2)编写并安装int 7ch中断例程,功能为完成loop指令功能

Program ex-13-2 参考解法

;名称:int 7ch中断例程 

;功能:完成loop指令功能,中断例程安装在0:200处

;参数:(cx)=循环次数,(bx)=位移

assume cs:code   
code segment  
start:  mov ax,cs  
        mov ds,ax  
        mov si,offset lp               ;设置ds:si指向源地址  
        mov ax,0  
        mov es,ax  
        mov di,200h                     ;设置es:di指向目标地址  
        mov cx,offset lpend-offset lp   ;设置cx为传输长度  

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  
lp:     push bp
        mov bp,sp 
        dec cx 

jcxz lpret

        add [bp+2],bx 
lpret:  pop bp  

iret

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

end start

;实验13(2)应用举例

assume cs:code   
code segment  
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
        int 7ch                   ;(cx)≠0,转移到标号s处

se: nop

        mov ax,4c00h  
        int 21h  
code ends  

end start

(3)补全下面的程序,分别在屏幕的第2、4、6、8行显示4句英文诗,补全代码。

assume cs:code

code segment

  s1:  db 'Good,better,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
        mov cx,4
ok:     mov bh,0        ;第0页
        mov dh,[si]     ;dh中放行号
        mov dl,0        ;dl中放列号
        mov ah,2        ;置光标
        int 10h

        mov dx,[bx]     ;ds:dx指向字符串首地址
        mov ah,9        ;在光标位置显示字符
        int 21h
        inc si          ;行号递增
        add bx,2        ;指向下一字符串
        loop ok

        mov ax,4c00h
        int 21h

code ends

end start

实验14 访问CMOS RAM

编程,以“年/月/日 时:分:秒”的格式,显示当前的日期、时间

Program ex-14-1 参考解法

assume cs:code
code segment  
start:  mov ax,0
        mov al,9
        mov si,0
        mov cx,6
s:      push cx
        push ax

out 70h,al ;将al送入端口70h

in al,71h ;从端口71h处读出单元内容

        mov ah,al
        mov cl,4
        shr ah,cl
        and al,00001111b  ;al分成两个表示BCD码值的数据
        add ah,30h
        add al,30h        ;BCD码+30h=10进制数对应的ASCII码
        mov bx,0b800h
        mov es,bx
        mov byte ptr es:[160*12+40*2][si],ah     ;显示十位数码
        mov byte ptr es:[160*12+40*2+2][si],al   ;显示个位数码
        pop ax
        dec ax            ;指向前一数据单元
        jmp s1
s0:     pop cx
        add si,6
        loop s
        mov ax,4c00h  
        int 21h 
s1:     cmp ax,10
        ja s0
        cmp ax,0
        je s0
        cmp ax,6
        ja s2         ;ax>6,为年/月/日
        je s3         ;ax=6,为日结尾
        jb s4         ;ax<6,为时:分:秒
s2:     mov byte ptr es:[160*12+40*2+4][si],'/'  ;添加'/'
        jmp s0
s3:     sub ax,2
        jmp s0
s4:     sub ax,1
        mov byte ptr es:[160*12+40*2+4][si],':'  ;添加':'
        jmp s0
code ends 

end start

实验15 安装新的int 9中断例程(按'A'键后显示满屏幕的'A')

;任务:安装一个新的int 9中断例程

;功能:在DOS下,按'A'键后除非不再松开,如果松开,就显示满屏幕的'A',其他键照常处理

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;设置ds:si指向源地址
mov di,204h;设置es:di指向目标地址
mov cx,offset int9end-offset int9;设置cx为传输长度

cld;设置传输方向为正

rep movsb

;将原来的int 9中断例程的入口地址保存在ds:200、ds:202单元中

push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]

;在中断向量表中设置新的int 9中断例程的入口地址

cli;设置IF=0屏蔽中断

mov word ptr es:[9*4],204h
mov word ptr es:[9*4+2],0

sti;设置IF=1不屏蔽中断

;结束

mov ax,4c00h
int 21h

;新的int 9中断例程

int9:push ax
push bx
push cx
push es

in al,60h;从端口60h读出键盘输入

;对int指令进行模拟,调用原来的int 9中断例程

pushf;标志寄存器入栈

call dword ptr cs:[200h];CS,IP入栈,(IP)=cs:[200h],(CS)=0

;如果是A断码,改变当前屏幕的显示字符

cmp al,9eh;和A的断码(1eh+80h)比较
jne int9ret;不等于A时转移
mov ax,0b800h
mov es,ax
mov bx,0
mov cx,2000
s:mov byte ptr es:[bx],41h;将A的ASCII码写入,改变字符
add bx,2
loop s
int9ret:pop es
pop cx
pop bx
pop ax

iret

int9end:nop

code ends

end start

实验16 包含多个功能子程序的中断例程

安装一个新的int 7ch中断例程,为显示输出提供功能子程序。

(1)清屏;

(2)设置前景色;

(3)设置背景色;

(4)向上滚动一行;

入口参数说明如下。

(1)用ah寄存器传递功能号:0表示清屏,1表示设置前景色,2表示设置背景色,3表示向上滚动一行;

(2)对于2、3号功能,用al传送颜色值,(al)∈{0,1,2,3,4,5,6,7}。

;任务:安装一个新的int 7ch中断例程

;功能:为显示输出提供功能子程序

;参数:ah传递功能号;0表示清屏,1表示设置前景色,2表示设置背景色,3表示向上滚动一行,对于2、3号功能,用al传送颜色值,(al)∈{0,1,2,3,4,5,6,7}

assume cs:code

code segment

start:mov ax,cs
mov ds,ax
mov si,offset s;设置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h;设置es:di指向目标地址
mov cx,offset send-offset s;设置cx为传输长度

cld;设置传输方向为正

rep movsb

;在中断向量表中设置新的int 7ch中断例程的入口地址

cli;设置IF=0屏蔽中断

mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0

sti;设置IF=1不屏蔽中断

;结束

mov ax,4c00h
int 21h

;新的int 7ch中断例程

s:jmp short set

tabledw sub1-s+200h,sub2-s+200h,sub3-s+200h,sub4-s+200h

set:push bx
cmp ah,3;判断ah中的功能号是否大于3
ja sret
mov bl,ah
mov bh,0
add bx,bx;计算对应子程序在table表中的偏移
call word ptr table[bx];调用对应的功能子程序
sret:pop bx

iret

;功能:清屏

sub1:push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,0
mov cx,2000
subles:mov byte ptr es:[bx],' '
add bx,2
loop subles
pop es
pop cx
pop bx

ret

;设置前景色,al传送颜色值,(al)∈{0,1,2,3,4,5,6,7}

sub2:push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub2s:and byte ptr es:[bx],11111000b
or es:[bx],al
add bx,2
loop sub2s
pop es
pop cx
pop bx

ret

;设置背景色,al传送颜色值,(al)∈{0,1,2,3,4,5,6,7}

sub3:push ax
push bx
push cx
push es
mov cl,4
shl al,cl
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub3s:and byte ptr es:[bx],10001111b
or es:[bx],al
add bx,2
loop sub3s
pop es
pop cx
pop bx
pop ax

ret

;向上滚动一行

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行

cld

mov cx,24;共复制24行
sub4s:push cx
mov cx,160

rep movsb;复制

pop cx
loop sub4s
mov cx,80
sub4s1:mov byte ptr [160*24+si],' ';最后一行清空
add bx,2
loop sub4s1
pop ds
pop es
pop di
pop si
pop cx

ret

send:nop

code ends

end start

实验17 通过逻辑扇区号对软盘进行读写

;功能:安装新的int 7ch中断例程,实现通过逻辑扇区号对软盘进行读写

;入口参数:

;(dx)=读写扇区的逻辑扇区号

;(ah)=0表示读扇区,(ah)=1表示写扇区

;es:bx指向存储读出数据或写入数据的内存区

;返回参数:

;(ah)=int 13h的功能号(2表示读扇区,3表示写扇区)

;(ch)=磁道号

;(cl)=扇区号

;(dh)=磁头号(面号)

assume cs:code

code segment

start:mov ax,cs
mov ds,ax
mov si,offset s;设置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h;设置es:di指向目标地址
mov cx,offset send-offset s;设置cx为传输长度

cld;设置传输方向为正

rep movsb

;在中断向量表中设置新的int 7ch中断例程的入口地址

cli;设置IF=0屏蔽中断

mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0

sti;设置IF=1不屏蔽中断

;结束

mov ax,4c00h
int 21h

;新的int 7ch中断例程

s:cmp ah,0
je s1
cmp ah,1
je s2
ja sret;ah大于1,结束
cmp dx,1443
ja sret;逻辑扇区号大于1443,结束

s3:push bx
mov ax,dx;逻辑扇区号赋值ax
mov dx,0
mov cx,1440
div cx;商在ax;余在dx
push ax;ax=面号=int(逻辑扇区号/1440),入栈

mov ax,dx
mov cx,18
div cx;商在ax;余在dx
push ax;ax=磁道号=int(rem(逻辑扇区号/1440)/18),入栈

add dx,1;dx=扇区号=rem(rem(逻辑扇区号/1440)/18)+1

mov cl,dl;扇区号赋值cl
pop bx;磁道号出栈
mov ch,bl
pop bx;面号出栈
mov dh,bl
pop bx;功能号出栈
mov ah,bl

sret:pop bx

iret

s1:mov ah,2;int 13h的功能号(ah=2表示读扇区)
push ax;功能号,入栈
jmp s3
s2:mov ah,3;int 13h的功能号(ah=3表示写扇区)
push ax;功能号,入栈
jmp s3
send:nop 
code ends 

end start

课程设计 2

·阅读下面的材料:

开机后,CPU自动进入到FFFF:0单元处执行,此处有一条跳转指令。CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。

初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。

硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导。

如果设为从软盘启动操作系统,则int 19h将主要完成以下工作。

(1)控制0号软驱,读取软盘0道0面1扇区的内容到0:7c00;

(2)将CS:IP指向0:7c00。

软盘的0道0面1扇区中装有操作系统引导程序。int 19h将其装到0:7c00处后,设置CPU从0:7c00开始执行此处的引导程序,操作系统被激活,控制计算机。  

如果在0号软驱中没有软盘,或发生软盘I/O错误,则int 19h将主要完成以下工作。

(1)读取硬盘C的0道0面1扇区的内容到0:7c00;

(2)将CS:IP指向0:7c00。

这次课程设计的任务是编写一个可以自行启动的计算机,不需要在现有操作系统环境中运行的程序。

该程序的功能如下:

(1)列出功能选项,让用户通过键盘进行选择,界面如下

1) reset pc ;重新启动计算机

2) start system ;引导现有的操作系统

3) clock ;进入时钟程序

4) set clock ;设置时间

(2)用户输入"1"后重新启动计算机(提示:考虑ffff:0单元)

(3)用户输入"2"后引导现有的操作系统(提示:考虑硬盘C的0道0面1扇区)。

(4)用户输入"3"后,执行动态显示当前日期、时间的程序。

显示格式如下:年/月/日 时:分:秒

进入此项功能后,一直动态显示当前的时间,在屏幕上将出现时间按秒变化的效果(提示: 循环读取CMOS)。

当按下F1键后,改变显示颜色;按下Esc键后,返回到主选单(提示:利用键盘中断)。

(5)用户输入"4"后可更改当前的日期、时间,更改后返回到主选单(提示:输入字符串)。

·参考程序

assume cs:code

code segment

;===============================================================

;功能:将代码写入0面0道1扇区

;入口参数:

;(ah)=int 13h的功能号(2表示读扇区,3表示写扇区)

;(al)=写入的扇区数

;(ch)=磁道号

;(cl)=扇区号

;(dh)=磁头号(面号)

;(dl)=驱动器号 软区从0开始,0:软驱A,1:软驱B

; 硬盘从80h开始,80h:硬盘C,81h:硬盘D

;es:bx指向将写入磁盘的数据或指向接收从扇区读入数据的内存区

;返回参数:

;操作成功:(ah)=0,(al)=写入的扇区数

;操作失败:(ah)=出错代码

;===============================================================

start:

mov ax,floppyend-floppy
mov dx,0
mov bx,512
div bx;商ax为所需的扇区数
inc al;写入的扇区数(为余数加扇区)

push cs
pop es
mov bx,offset floppy;es:bx指向要被写入的内存单元

mov ch,0;磁道号
mov cl,1;扇区号
mov dl,0;驱动器号,软盘A
mov dh,0;磁头号(面号)
mov ah,3;int 13h的功能号(3表示写扇区)
int 13h;将代码写入0面0道1扇区

mov ax,4c00h
int 21h

floppy:

jmp read

;===============================================================

;直接定址表

;===============================================================

tabledw function1-floppy,function2-floppy

dw function3-floppy,function4-floppy

menudb '***main Menu***',0

db '1) reset pc    ',0
db '2) start system',0
db '3) clock       ',0
db '4) set clock   ',0
db 'Please Choose: ',0

timedb 'YY/MM/DD hh:mm:ss',0

cmosdb 9,8,7,4,2,0

hintdb 'press F1 to change the color, press Esc to return',0

hint1db 'Please input: YY/MM/DD hh:mm:ss',0

chardb ' / / : : ',0

;===============================================================

;功能:将0面0道2扇区的内容写入0:7e00h

;入口参数:

;(ah)=int 13h的功能号(2表示读扇区)

;(al)=写入的扇区数

;(ch)=磁道号

;(cl)=扇区号

;(dh)=磁头号(面号)

;(dl)=驱动器号 软区从0开始,0:软驱A,1:软区B

; 硬盘从80h开始,80h:硬盘C,81h:硬盘D

;es:bx指向将写入磁盘的数据

;返回参数:

;操作成功:(ah)=0,(al)=写入的扇区数

;操作失败:(ah)=出错代码

;===============================================================

read:

mov ax,floppyend-floppy
mov dx,0
mov bx,512
div bx;商ax为所需的扇区数
inc al

mov bx,0
mov es,bx
mov bx,7e00h;es:bx指向要读入的内存单元

mov ch,0;磁道号
mov cl,2;扇区号
mov dl,0;驱动器号
mov dh,0;磁头号(面号)
mov ah,2;int 13h的功能号(2表示读扇区)
int 13h;读取0面0道2扇区的内容到0:7e00h处

;(第二扇区从512/200h开始)

mov ax,7c0h
push ax;push cs
mov ax,showmenu-floppy
push ax;push ip
retf;jmp showmenu

;***************************************************************

;显示主菜单,调用show_str、clean子程序

;***************************************************************

showmenu:

call clean;清屏
push cs
pop ds
mov si,menu-floppy
mov dh,8
mov dl,30
mov cx,6

showmenu0:

push cx
mov cl,2
call show_str
add si,16
inc dh
pop cx
loop showmenu0

;===============================================================

;接收键盘输入,跳转相应功能程序段

;调用BIOS用来提供读取键盘缓冲区功能的中断例程int 16h,

;将读取的扫描码送入ah,ASCII码送入al

;===============================================================

go:mov ax,0
int 16h
cmp al,'1'
jb showmenu
cmp al,'4'
ja showmenu

sub al,31h
mov bl,al
mov bh,0
add bx,bx
add bx,3;计算相应子程序在table中的位移
call word ptr cs:[bx]

jmp showmenu

;***************************************************************

;功能1:重新启动计算机

;***************************************************************

function1:

mov ax,0ffffh
push ax
mov ax,0
push ax
retf;jmp ffff:0

;***************************************************************

;功能3:进入时钟程序

;***************************************************************

function3:

push ax
push bx
push cx
push dx
push si
push ds
push es
call clean;清屏
mov dh,0;行号
mov dl,0;列号
mov cl,2
mov si,offset hint-floppy
call show_str;显示提示信息

;===============================================================

;名称:clock

;功能:动态显示当前日期、时间

;===============================================================

mov cx,2;显示颜色
clock:mov bx,offset cmos-floppy
mov si,offset time-floppy
push cx
mov cx,6
clock0:push cx
mov al,[bx]

out 70h,al;将al送入地址端口70h

in al,71h;从数据端口71h处读出单元内容

mov ah,al
mov cl,4
shr al,cl;右移4位
and ah,0fh;al分成两个表示BCD码值的数据
add ax,3030h;BCD码+30h=10进制数对应的ASCII码
mov [si],ax;ASCII码写入time段
inc bx
add si,3
pop cx
loop clock0

;---------------------------------------------------------------

;按下F1键后,改变显示颜色

;按下Esc键后,返回主菜单,其他键照常处理

;---------------------------------------------------------------

mov al,0

in al,60h

pop cx;显示颜色
cmp al,3bh;和F1的扫描码3bh比较
je colour
cmp al,1;和esc的扫描码01h比较
je clockend
jmp show_clock
col_1:mov cx,1;cl∈[1,7],0为黑色
jmp show_clock
colour:cmp cx,7
je col_1
inc cx

show_clock:

mov dh,12;行号
mov dl,30;列号
mov si,offset time-floppy
call show_str
jmp clock;循环显示CMOS

clockend:

pop es
pop ds
pop si
pop dx
pop cx
pop bx
pop ax

ret

;***************************************************************

;功能4:设置时间

;***************************************************************

function4:

push ax
push bx
push cx
push dx
push si
call clean

mov dh,8;行号
mov dl,30;列号
mov cl,2
mov si,offset hint1-floppy
call show_str;显示提示信息

add dh,1;行号
add dl,14;列号
mov si,offset char-floppy;ds:si指向字符栈空间
call show_str;显示输入格式
mov di,0
call getstrs
call witein
call cleanchar
pop si
pop dx
pop cx
pop bx
pop ax

ret

;===============================================================

;清除char内输入数据,还原环境

;===============================================================

cleanchar:

push cx

cleanchar1:

mov cx,di

jcxz cleanchar2

call charpop
jmp cleanchar1

cleanchar2:

pop cx

ret

;===============================================================

;ASCII=>BCD,写入CMOS

;===============================================================

witein:

push si
mov cx,6
mov bx,offset cmos-floppy
wite:push cx
mov al,[bx]

out 70h,al;将al送入地址端口70h

mov ax,[si]
sub ah,30h;10进制数对应的ASCII码-30h=BCD码
sub al,30h
mov cl,4
shl al,cl;左移4位
add al,ah;al为8位BCD码

out 71h,al;从数据端口71h处写入单元内容

add si,3
inc bx
pop cx
loop wite
pop si

ret

;===============================================================

;子程序:接收数字输入

;参数说明:di=char栈顶(字符地址、个数记录器)

;===============================================================

getstrs:

push ax
push bx
getstr:mov ax,0
int 16h
cmp ah,0eh;退格键的扫描码
je backspace
cmp ah,1ch;Enter键的扫描码
je enter1
cmp al,'0'
jb getstr
cmp al,'9'
ja getstr
cmp di,16;限制输入个数
ja enter1
call charpush;字符入栈
call show_str;显示栈中字符
jmp getstr

backspace:

call charpop;字符出栈
call show_str;显示栈中字符
jmp getstr
enter1:call show_str;显示栈中字符
pop bx
pop ax

ret

;===============================================================

;子程序:数字的入栈。

;参数说明:ds:si指向char栈空间;(al)=入栈字符;

;===============================================================

charpush:

mov bx,di
mov [si][bx],al
inc di
cmp di,2
je adds
cmp di,5
je adds
cmp di,8
je adds
cmp di,11
je adds
cmp di,14
je adds

ret

adds:inc di

ret

;===============================================================

;子程序:数字的出栈。

;参数说明:ds:si指向char栈空间;(al)=入栈字符;

;===============================================================

charpop:cmp di,0
je sret;栈顶为0(无字符),结束
cmp di,3
je subs
cmp di,6
je subs
cmp di,9
je subs
cmp di,12
je subs
cmp di,15
je subs
dec di
mov bx,di
mov al,' '
mov [si][bx],al

ret

subs:sub di,2
mov bx,di
mov al,' '
mov [si][bx],al

ret

sret:ret

;===============================================================

;名称:show_str子程序

;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。

;参数:(dh)=行号(取值范围0~24);

; (dl)=列号(取值范围0~79);

; (cl)=颜色;

; ds:si指向字符串的首地址。

;返回:无。

;===============================================================

show_str:

push ax
push bx
push cx
push dx
push si
push es
mov ax,0b800h
mov es,ax
mov ax,160
mul dh
mov bx,ax;bx=160*dh
mov ax,2
mul dl;ax=dl*2
add bx,ax;mov bx,(160*dh+dl*2)设置es:bx指向显存首地址
mov al,cl;把颜色cl赋值al
mov cl,0
show0:mov ch,[si]

jcxz show1;(ds:si)=0时,转到show1执行

mov es:[bx],ch
mov es:[bx].1,al
inc si;ds:si指向下一个字符地址
add bx,2;es:bx指向下一个显存地址
jmp show0
show1:pop es
pop si
pop dx
pop cx
pop bx
pop ax

ret

;===============================================================

;名称:clean子程序

;功能:清屏

;===============================================================

clean:push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,0
mov cx,2000
clean0:mov byte ptr es:[bx],' '
add bx,2
loop clean0
pop bx
pop cx
pop es

ret

;***************************************************************

;功能2:引导现有的操作系统

;***************************************************************

;===============================================================

;功能2实现引导现有的操作系统,代码需要将硬盘的0面0道1扇区读入0:7c00,

;会覆盖从软盘读到0:7c00的第一个扇区,所以功能2代码不能写在第一个扇区

;===============================================================

function2:

call clean
mov ax,0
mov es,ax
mov bx,7c00h

mov al,1;读取的扇区数
mov ch,0;磁道号
mov cl,1;扇区号
mov dl,80h;驱动器号
mov dh,0;磁头号(面号)
mov ah,2;int 13h的功能号(2表示读扇区)
int 13h;读取0面0道1扇区的内容到0:7c00h处

mov ax,0
push ax;push cs
mov ax,7c00h
push ax;push ip
retf;jmp 0:7c00h

floppyend:nop

code ends 

end start

提纲正文到此结束

提纲版本:2024-05-04版

以上为16位汇编,有些可以迁移到32位和64位,后面会更新更多帖子

发表评论

滚动至顶部