专注于操作系统23之中断进阶

     在这里我们要实现一个硬件中断,计时器中断。在上一篇文章中我们屏蔽了所有的可屏蔽的中断,在这将打开IRQ0中断,即时钟中断,只需把送入OCW1的内容由原来的11111111b改为11111110b即可。还要修改一下IDT表的内容,增加一个处理时钟中断的程序。所以,我们只需在上篇的代码中修改几行即可。该程序实现的效果是在屏幕的第一行第70列,出现一个不断变化的字符(时钟改变一次,字符也变化一次)。

    下面是代码, 参考了《自己动手写操作系统》


;;nasm 2.07
;;nasm mytimebreak.asm -o mytimebreak.com
org 0100h

%macro Descriptor 3             ;定义Descriptor结构体宏
	dw	%2 & 0FFFFh				; 段界限 1				(2 字节)
	dw	%1 & 0FFFFh				; 段基址 1				(2 字节)
	db	(%1 >> 16) & 0FFh			; 段基址 2				(1 字节)
	dw	((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)	; 属性 1 + 段界限 2 + 属性 2		(2 字节)
	db	(%1 >> 24) & 0FFh			; 段基址 3				(1 字节)
%endmacro ; 共 8 字节
                                 ;定义Gate结构体宏
%macro Gate 4
	dw	(%2 & 0FFFFh)				; 偏移 1				(2 字节)
	dw	%1					; 选择子				(2 字节)
	dw	(%3 & 1Fh) | ((%4 << 8) & 0FF00h)	; 属性					(2 字节)
	dw	((%2 >> 16) & 0FFFFh)			; 偏移 2				(2 字节)
%endmacro ; 共 8 字节


DA_C		EQU	98h	; 只读代码段的属性值
DA_DRW		EQU	92h	; 允许读写的数据段的属性值
DA_DRWA		EQU	93h	; 存在的已访问可读写数据段类型值
DA_32		EQU	4000h	;32位段的属性值
DA_386IGate	EQU	  8Eh	; 386 中断门类型值



	jmp	LABEL_BEGIN         
	               ;现在进行第一步:建立GDT表
[SECTION .gdt]     ;用于放GDT表的段
;GDT
LABEL_GDT: Descriptor 0,0,0 ;空描述符,当一个任务没有LDT时,会把LDTR清空,这时,LDTR便指向空描述符。

LABEL_CODE32: Descriptor 0,SegCode32Len-1,DA_C + DA_32  ; 代码段的描述符
LABEL_GSTACK: Descriptor 0, GStackLen , DA_32 + DA_DRWA      ;全局堆栈段的描述符

LABEL_VIDEO: Descriptor 0B8000h,0ffffh,DA_DRW    ;显存的描述符,0b8000h是显存的首地址
;GDT 结束


GdtLenth equ $ - LABEL_GDT   ;计算GDT表的长度

GdtPtr dw GdtLenth - 1 ; 准备存入GDTR的信息
       dd 0    ;    用于存放GDT表的物理地址,后面会计算

;GDT选择子

SelectorCode32 equ LABEL_CODE32 - LABEL_GDT ;代码段的选择子
SelectorVideo  equ LABEL_VIDEO - LABEL_GDT  ;显存所在段的选择子
SelectorGStack equ	LABEL_GSTACK - LABEL_GDT       ;全局堆栈段的选择子

;[SECTION .gdt]结束


;建立IDT表
[SECTION .idt]
ALIGN 32
[BITS 32]
LABEL_IDT:
%rep 32    ;利用NASM的%rep预处理指令,创建32个完全相同的描述符,都指向(SelectorCode32:Show)
        Gate SelectorCode32, ShowOffset, 0 ,DA_386IGate
%endrep

.20h:   Gate SelectorCode32, TimeOffset, 0 ,DA_386IGate ;计时器中断的描述符

%rep 222    ;利用NASM的%rep预处理指令,创建222个完全相同的描述符,都指向(SelectorCode32:Show)
        Gate SelectorCode32, ShowOffset, 0 ,DA_386IGate
%endrep

IdtLen equ $ - LABEL_IDT  ;准备用于加载IDTR寄存器的 IdtPtr
IdtPtr  dw IdtLen - 1    ;IDT表的段限
        dd 0             ;IDT表的基地址
;[SECTION .idt] 结束

[SECTION .gstack]
ALIGN	32
[BITS	32]
LABEL_SEG_GSTACK:
times 512  db 0
GStackLen equ $ - LABEL_SEG_GSTACK - 1
;[SECTION .gstack]结束



[SECTION .s16]
[BITS 16]	;目标处理器模式
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h







;初始化全区堆栈段的描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_GSTACK
mov	word [LABEL_GSTACK + 2], ax
shr	eax, 16
mov	byte [LABEL_GSTACK + 4], al
mov	byte [LABEL_GSTACK + 7], ah

;初始化32位段的描述符

xor eax,eax   ; 同或运算,这里是将eax清零

mov ax,cs
shl eax,4     ;左移四位,相当于乘以16,实模式下计算物理地址
add eax,LABEL_SEG_CODE32    ;eax中为32位段的物理地址

mov word [LABEL_CODE32 + 2],ax   ;将32位段的物理地址存入段的描述符中

shr eax,16
mov byte [LABEL_CODE32 + 4],al
mov byte [LABEL_CODE32 + 7],ah

;为加载IDTR做准备
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_IDT
mov dword [IdtPtr + 2],eax


;计算GDT表的物理地址,将GDT表的物理地址信息存入GdtPtr中
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_GDT
mov dword [GdtPtr + 2],eax

;现在进行第二步:将GDT表的信息加载到GDTR中
lgdt [GdtPtr]  ;装载GDTR

;加载IDTR
lidt [IdtPtr]

;关中断
cli

;现在进行第三步:打开地址线
in al,92h
or al,00000010b
out 92h,al

;现在进行第四步:置PE为1,表示CPU处于保护模式下
mov eax,cr0
or eax,1
mov cr0,eax
;现在进入第五步:跳到保护模式的代码

jmp dword SelectorCode32:0  ;这里将SelectorCode32存入CS中作为选择子
;[SECTION .s16] 结束

[SECTION .s32]
[BITS 32]  

LABEL_SEG_CODE32:
mov ax,SelectorVideo ;视频选择子,用于找到显存段的描述符
mov gs,ax 
mov ax,SelectorGStack
mov ss,ax
mov esp,GStackLen

mov edi,(80 * 10 + 0)*2  ;屏幕的第10行,第0列
mov ah,0Ch
mov al,'p'
mov [gs:edi],ax ;将字符‘p’输出到屏幕
 
call Init8259A

int 080h ;因为我们的IDT表中的描述符都是指向SelectorCode32:Show的
         ;所以,int 80h 将执行show处的程序,显示一个‘!’字符
sti
jmp $
Init8259A:       ;初始化8259
  mov al,011h
  out 020h,al      ;将al的内容送到,主8259的ICW1中
  call io_delay    ;后面的out指令也是把al的内容送到相应的ICW

  out 0a0h,al      ;从8259,ICW1
  call io_delay   

  mov al,020h      ;IRQ0对应中断向量 20h 设置完成后IRQ0~IRQ7就对应中断向量20h~27h
  out 021h,al      ;主8259,ICW2
  call io_delay

  mov al,028h     ;IRQ8对应中断向量 28h  设置完成后IRQ8~IRQ15就对应中断向量28h~2fh
  out 0a1h,al     ;从8259,ICW2
  call io_delay

  mov al,004h     ;IR2 对应从8259 (通过ICW3将两片(主和从)8259芯片连起来)
  out 021h,al     ;主8259,ICW3
  call io_delay

  mov al,002h     ;对应主8259的IR2
  out 0a1h,al     ;从8259,ICW3
  call io_delay
  
  mov al,001h
  out 021h,al   ;主8259,ICW4
  call io_delay

  out 0a1h,al     ;从8259,ICW4
  call io_delay

  mov al,11111110b  ;仅打开定时器中断
  out 021h,al       ;将al的内容送到主8259的OCW1中
  call io_delay

  mov al,11111111b  ;屏蔽从8258的所有中断
  out 0a1h,al       ;从8259,OCW1
  call io_delay
  ret

  io_delay:
  nop
  nop
  nop
  nop

  ret


Time:   ;处理时钟中断的程序
TimeOffset equ Time - $$


inc byte [gs:((80*0+70)*2)]
mov al,20h
out 20h,al      ;发送EIO,表示中断处理完成,可以处理下一个中断了

iretd

Show:
ShowOffset equ Show - $$
mov ah,0ch
mov al,'!'
mov [gs:((80*0+10)*2)],ax


iretd
SegCode32Len equ $ - LABEL_SEG_CODE32  ;计算32位代码段的长度
;[SECTION .s32]结束


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值