8086CPU_Learning(9)

25 January 2025
8 mins

昨天学的那个太难了,再加上下午一直在玩,今天要赶赶进度

int 指令

当CPU执行 int n指令时,相当于引发一个n号中断的过程,其执行流程如下:

可以在程序中使用int指令,调用任何一个中断的中断处理程序

比如我们使用这段程序:

assume cs:code
code segment
start:
    mov ax,0b800h
    mov es,ax
    mov byte ptr es:[12*160+40*2],'!'
    int 0
code ends
end start

当我们运行这段指令之后我执行0号处理程序,然后回到系统

由此,我们可以看出int 和 call指令相似,都是调用一段程序

我们可以实现编译一些子程序,作为中断处理程序,然后用int进行调用,我们把这个称为中断例程

编写供应用程序调用的中断例程

书本里面给了两个例题

image.png

我们在下面给出安装程序

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
    iret	;中断例程的最后使用,退栈CS:IP和标志寄存器
sqrend:
    nop

code ends
end start

image.png

我们在下面给出安装程序

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],的
    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
    and byte ptr [si],11011111b
    inc si
    jmp short change
ok:
    pop si
    pop cx 
    iret
capitalend:
    nop

code ends
end start

因为中断例程用到了si,cx所以我们需要用栈保存

对int,iret和栈的深入理解

我们通过一个例子,来实现int,iret,栈的深入了解

image.png

为了具备loop的功能,7cH中断例程需要具备以下功能:

可是怎么实现到目的地的转移到呢?

由此我们可以写出中断例程:

lp:
	push bp
	mov bp,sp
	dec cx
	jcxz lpret
	add [bp+2],bx
lpret:
	pop bp
	iret 

这需要说明一下,因为我们要访问栈,所以使用了bp.在程序开始前,先将bp入栈保存,结束时再出栈恢复.当要修改栈中se的偏移地址时,栈中的结构是: 栈顶处是bp原来的数值,下面是se的偏移地址,在下面是s的段地址,再下面是标志寄存器此时bp中为栈顶的偏移地址,所以((ss)*16+(bp)+2)处为se的偏移地址,再加上bx中的偏移位移就变成了s的偏移地址.最后再用iret出栈返回,CS:IP此时为标号s的指令

BIOS与DOS

BIOS和DOS提供的中断例程

在系统的ROM中存放了一套程序,称之为BIOS(基本输入输出系统),BIOS中主要包含以下内容:

操作系统DOS也提供了中断例程,从操作系统的角度来看,DOS的中断例程就是操作系统像程序员提供的一种编程资源

BIOS和DOS中断例程的安装过程

BIOS和DOS 的中断例程是怎么安装到内存中的呢?

BIOS中断例程的应用

我们可以使用int 10H中断例程,其包含了多个和屏幕输出相关的子程序.

这里可以看出,一个供程序员调用的中断例程,其中包含了多个子程序,中断例程内部用传递进来的参数来决定执行哪一个子程序.在BIOS和DOS中提供的中断例程,都是用ah来传递内部的子程序的编号

展示以下 int 10h中断例程的设置光标位置功能:

mov ah,2	;置光标
mov bh,0	;第0页
mov dh,5	;dh中放行号
mov dl,12	;dl中放列号
int 10h
;上面操作的含义是:设置光标到第0页,第5行,第12列

关于页号的含义可以看看下方的图片:

image.png

我们继续试试int 10h中断例程的在光标位置显示字符功能

mov ah,9	;在光标位置显示字符
mov al,'a'	;字符
mov bl,7	;颜色属性
mov bh,0	;第0页
mov cx,3	;字符重复个数
int 10H
;含义:在屏幕的第5行12列显示3个红底高亮闪烁绿色的"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,3
    int 10h

    mov ax,4c00H
    int 21h

code ends
end start  

非常成功(好耶!!!)

DOS中断例程的应用

int 21H是DOS提供的中断例程,其中包含了DOS提供给程序员编程时调用的子程序

比如我们常用的:

mov ah,4ch	;程序返回
mov al,0	;程序返回值
int 21h

我们来试试另外的用法:在光标位置显示字符串的功能

;ds:dx指向字符串	;要显示的字符串需要用"$"作结束符
mov ah,9		;功能号9,表示在光标位置显示字符串
int 21h

我们来试试效果

assume cs:code
data segment
    db 'I love you my baby','$' ;$本身并不输出,只是起到边界作用
code segment
start:
    mov ah,2
    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  

DOS为程序员提供了许多可以调用的子程序,包含在int 21H中断例程中.可以自行了解

端口

在PC机种,和CPU通过总线相连接的芯片除了各种储存器以外,还有以下3种芯片:

在这些芯片种,都有一组可以由CPU读写的寄存器,这些寄存器有以下共同点:

所以,从CPU的角度,将这些寄存器当作端口,对他们进行统一编址,从而建立一个统一的端口地址空间。每一个端口在地址空间都有一个地址

CPU可以直接读写这三个地方的数据:

端口的读写

在访问端口时,CPU通过端口地址来定位端口。因为端口所在的芯片和CPU通过总线相互连接,所以,端口地址和内存地址一样,通过地址总线来传送。在PC中CPU最多可以定位64KB个不同的端口,即端口的地址范围为0~65535

访问端口的命令只有两条 in 和 out,我们分析下面的例子:

in al,60H	;从60H号端口读入一个字节

注意,在in 和 out指令中,只能使用ax和al来存放从端口中读入的数据或要发送到端口中的数据

CMOS RAM芯片

我们通过一个芯片的例子来详细的体会一下对端口的访问

这个芯片的特征如下:

芯片的作用如下:

也就是说CPU对CMOS的读写要分两步进行。比如,读CMOS的2号单元

  1. 将2送入端口70H
  2. 从端口71H读出2号单元的内容

shl和shr指令

他们两个是逻辑移位指令

他们的使用方法为

shl/shr 二进制数据,见下面的分类
;如果只是移动一位
shl al,1
;如果移动的不止一位,则将移动的次数放在cl中
mov cl,N
shl al,cl

shl

shl是逻辑左移指令,它的功能为:

将X逻辑左移一位相当于进行: X=X*2

shr

shr是逻辑右移指令,它的功能为:

将X逻辑右移一位相当于进行:X=X/2

这里举个例子,将10100100B左移三位

原数据:	10100100
左移后:  10100100		CF =1
最低位用0补充:00100000

CMOS RAM中存储的时间信息

这里有一个编程任务

image.png

实现这个功能,我们需要分成两个步骤进行:

关键在于怎么将BCD值转换为十进制数对应的ASCII码,这里我们可以用刚刚提到的逻辑位移实现

assume cs:code 
code segment
start:
    ;从CMOS中单读出当前的月份的BCD码
    mov al,8
    out 70h,al
    in al,71h
    ;分离BCD码
    mov ah,al
    mov cl,4
    shr ah,cl
    and al,00001111b
    ;转换为ASCII码
    add ah,30H
    add al,30H
    ;显示
    mov bx,0b800H
    mov es,bx
    mov byte ptr es:[160*12+40*2],ah    ;显示十位数码
    mov byte ptr es:[160*12+40*2+2],al  ;显示个位数码

    mov ax,4c00H
    int 21H
code ends
end start

实验十四

image.png

assume cs:code 
data segment
    db '00/00/00 00:00:00','$'
    db 9,8,7,4,2,0
data ends
stack segment
    dw 8 dup(0)
stack ends
code segment
start:
    ;初始化
    mov ax,stack
    mov ss,ax
    mov sp,16
    mov ax,data
    mov ds,ax
    mov si,0
    mov cx,6
    mov bx,12H
    ;从CMOS中单读出当前的月份的BCD码
s:  
    mov al,[bx]
    out 70h,al
    in al,71h
    call go 
    inc bx
    loop s

    ;字符显示
    mov ax,data 
    mov ds,ax
    mov dx,0
    mov ah,9
    int 21H

    mov ax,4c00H
    int 21H

go:   
translation:
    ;分离BCD码
    push cx
    mov ah,al
    mov cl,4
    shr ah,cl
    and al,00001111b
    ;转换为ASCII码
    add ah,30H
    add al,30H
input:
    ;存入
    mov byte ptr ds:[si],ah    ;显示十位数码
    mov byte ptr ds:[si+1],al  ;显示个位数码
    add si,3
    pop cx
    ret

code ends
end start

← 初始图形学(9)

初始图形学(8) →