8253/4 定时器芯片实现时钟中断处理例程

实验要求

使用 nasm 汇编编程实现一个 MBR 程序,每隔 1 秒钟左右输出一个 (组)字符,具体内容包括:

1)实现一个时钟中断处理例程,按规定时间间隔输出字符(可使用 BIOS 提供的 int 10h 实现字符输 出);

2)修改中断向量表,将时钟中断处理例程起始地址填入中断向量表对应表项;

3)设置 8253/4 定时器芯片,每隔 20ms 左右(自定 < 1 秒)产生一次时钟中断。

基本数据设置

TIMER_FREQ equ 1193182
TARGET_FREQ equ 50
DIVISOR equ TIMER_FREQ / TARGET_FREQ

[BITS 16]
[ORG 0x7C00]

这段代码是用于设置定时器频率的。首先定义了两个常数:TIMER_FREQ(定时器频率)和 TAR_GET_FREQ(目标频率)。然后计算这两个频率之间的比率,并将结果存储在 DIVISOR(除数)变量 中。

[BITS 16] 表示汇编器以 16 位模式工作。在这种模式下,寄存器的大小为 16 位,寻址范围为 0x0000 到 0xFFFF。

[ORG 0x7C00] 表示代码段从地址 0x7C00 开始。在实模式下,程序从地址 0x0000 开始,但是由于 BIOS 将 CS:IP 初始化为 0x0000:0x0000,所以程序实际上从地址 0x7C00 开始执行。

基本设置

clear:;清屏
    mov ax, 0x600               ;ah中输入功能号
    mov bx, 0x700               ;设置上卷行属性,0x70表示用黑底白字的属性填充空白行
    mov cx, 0                   ;左上角: (0, 0)
    mov dx, 0x184f	            ;右下角: (80,25),
    int 0x10                    ;int 0x10

mouse:;让光标处于最左上角
    mov ah, 2		            ;输入: 2号子功能是设置光标位置,需要存入ah寄存器
    mov bh, 0		            ;bh寄存器存储的是待设置光标的页号
    mov dh, 0		            ;dh寄存器存储的是行号
    mov dl, 0		            ;dl寄存器存储的是列号
    int 0x10		            ;输出: 无

因为我们需要在屏幕上打印字符串,而原先的页面上有 booting from hard disk 等等内容,所以我们 需要先将原先的页面清屏,之后我们需要将光标移到左上角,这样子可以使得打印字符串从页面的最上面 开始打印。

初始化中断处理程序

set_interrupt_handler_assembly:   ;初始化中断处理程序
    CLI
    xor ax, ax
    mov ds, ax ; 将ds设置为0,因为IVT位于内存的最低端
    mov word [8*4], handle_interrupt_assembly ; 设置中断处理程序的偏移地址
    mov word [8*4+2], cs ; 设置中断处理程序的段地址
    STI ; 重新开启中断

首先,关闭中断(CLI),然后将一些寄存器清零,并将数据段(DS)设置为 0。这是因为中断向量 表(IVT)位于内存的最低端,所以我们需要将段地址(CS)设置为 0。接下来,它将中断处理程序的偏 移地址(timer_interrupt)写入 IVT 中的位置 84,并将中断处理程序的段地址(CS)写入 IVT 中的位 置 84+2。最后,重新开启中断(STI).

初始化计时器

init_timer:
    mov al, 0x36 ; 控制字到0x43
    out 0x43, al
    mov ax, DIVISOR ; 加载除数值
    out 0x40, al ; 通道0,用于产生时钟中断
    mov al, ah
    out 0x40, al

这段代码是用于初始化一个定时器。定时器用于产生时钟中断,从而实现时间控制。

计数器

wait_for_interrupt:
    hlt ; 等待中断
    jmp wait_for_interrupt ; 无限循环

handle_interrupt_assembly:
    INC CX
    CMP CX, 50
    JE Print
    mov al, 0x20 ; AL = 0x20 表示发送EOI信号给PIC
    out 0x20, al ; 发送EOI信号
    IRET ; 从中断返回

这段代码实现了一个简单的定时器中断。

首先,程序进入 wait_for_interrupt 函数,这里使用 hlt 指令使 CPU 暂停,然后使用 jmp 指令无限 循环等待中断。

当定时器中断发生时,程序会跳转到 timer_interrupt 函数。首先,程序会递增计数器 CX 的值,然 后比较 CX 和 50 的大小。如果 CX 等于 50,那么就跳转到 Print 函数。

在 timer_interrupt 函数中,程序将 AL 寄存器的值设置为 0x20,表示发送 EOI 信号给 PIC(可编 程中断控制器)。然后使用 out 指令将 EOI 信号发送给 PIC。最后,使用 IRET 指令从中断返回。

打印函数

Print:
    CLI
    XOR CX, CX
    mov ah, 0x0E ; teletype输出
    mov al, 'I' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, ' ' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, 'l' ; 显示字符
    int 0x10 ; BIOS视频服务

这边选择了逐个字符打印“I love OS”,最后打印一个换行即可,当然也可以将需要打印的data整体传入打印。

实现效果

将该汇编程序编译成bin文件后,用qemu运行该bin文件,实现效果如下:

程序源码如下:

TIMER_FREQ equ 1193182
TARGET_FREQ equ 50
DIVISOR equ TIMER_FREQ / TARGET_FREQ

[BITS 16]
[ORG 0x7C00]

clear:;清屏
    mov ax, 0x600               ;ah中输入功能号
    mov bx, 0x700               ;设置上卷行属性,0x70表示用黑底白字的属性填充空白行
    mov cx, 0                   ;左上角: (0, 0)
    mov dx, 0x184f	            ;右下角: (80,25),
    int 0x10                    ;int 0x10

mouse:;让光标处于最左上角
    mov ah, 2		            ;输入: 2号子功能是设置光标位置,需要存入ah寄存器
    mov bh, 0		            ;bh寄存器存储的是待设置光标的页号
    mov dh, 0		            ;dh寄存器存储的是行号
    mov dl, 0		            ;dl寄存器存储的是列号
    int 0x10		            ;输出: 无

set_interrupt_handler_assembly:   ;初始化中断处理程序
    CLI
    xor ax, ax
    mov ds, ax ; 将ds设置为0,因为IVT位于内存的最低端
    mov word [8*4], handle_interrupt_assembly ; 设置中断处理程序的偏移地址
    mov word [8*4+2], cs ; 设置中断处理程序的段地址
    STI ; 重新开启中断

init_timer:
    mov al, 0x36 ; 控制字到0x43
    out 0x43, al
    mov ax, DIVISOR ; 加载除数值
    out 0x40, al ; 通道0,用于产生时钟中断
    mov al, ah
    out 0x40, al

wait_for_interrupt:
    hlt ; 等待中断
    jmp wait_for_interrupt ; 无限循环

handle_interrupt_assembly:
    INC CX
    CMP CX, 50
    JE Print
    mov al, 0x20 ; AL = 0x20 表示发送EOI信号给PIC
    out 0x20, al ; 发送EOI信号
    IRET ; 从中断返回

Print:
    CLI
    XOR CX, CX
    mov ah, 0x0E ; teletype输出
    mov al, 'I' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, ' ' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, 'l' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, 'o' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, 'v' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, 'e' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, ' ' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, 'O' ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出mov ah, 0x0E ; teletype输出
    mov al, 'S' ; 显示字符
    int 0x10 ; BIOS视频服务
    
    ;换行
    mov ah, 0x0E ; teletype输出
    mov al, 0x0D ; 显示字符
    int 0x10 ; BIOS视频服务

    mov ah, 0x0E ; teletype输出
    mov al, 0x0A ; 显示字符
    int 0x10 ; BIOS视频服务

    STI
mov al, 0x20 ; AL = 0x20 表示发送EOI信号给PIC
out 0x20, al ; 发送EOI信号
IRET ; 从中断返回   

TIMES 510-($-$$) DB 0
DW 0xAA55

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值