大家一起写操作系统(3)-时钟中断

  大家都知道操作系统利用中断来与外设进行交互。80xx86兼容机使用两片级联的8259A可编程中断控制芯片组成一个中断控制器,用于实现与I/O设备的交互。可编程意味着我们可以为不同的外设(时钟,键盘,鼠标等)分配不同的中断号,从而执行不同的中断处理过程。

  BIOS初始化这个中断控制器时将中断号8分配给了时钟控制器,因而下面我们通过将中断向量表中的8号描述符设为我们的中断程序地址,进而实现时钟中断程序。我们在中断程序中打印字符'E',每打2000次后再从头开始打,为了能够看出重复打印的效果,满2000次后更换颜色。

 LATCH equ 11930
VIDEO_DS equ 0x18
[SECTION text]
[BITS 32]
startup_32:
    mov eax,0x10
    mov ds,ax
    lss esp,[init_stack] ;mem low->reg,mem high->ss
    call setup_idt    ;初始化IDT中断向量表
    call setup_gdt  ;初始化GDT
    mov  eax,0x10 ;初始化完GDT后重新加载ds,es,fs,gs,ss,sp
    mov  ds,ax
    mov  es,ax
    mov  fs,ax
    mov  gs,ax
    lss esp,[init_stack]
;设置8253定时芯片,把计数器通道0设置成每隔10ms向中断控制器发送一个中断请求
    mov al,0x36
    mov edx,0x43
    out dx,al
    mov eax,LATCH
    mov edx,0x40
    out dx,al
    mov al,ah
    out dx,al
    sti        ;开中断
loop:
    jmp loop

setup_gdt:
    lgdt [lgdt_opcode]
    ret
    
;begin setup_idt fun  ;初始化idt,256个描述符全初始化为打印字符函数
setup_idt:
    lea edx,[ignore_int]  ;中断入口偏移放入edx
    mov eax,0x00080000  ;eax高位为内核代码段选择子
    mov ax,dx           ;eax低位为中断入口偏移低位
    mov dx,0x8E00       ;edx低位为中断门定义.DPL=0
    lea edi,[idt]         ;得到idt表位置,准备初始化
    mov ecx,256         ;初始化256 IDT描述符
rp_idt:
    mov [edi],eax
    mov [edi+4],edx
    add edi,8
    dec ecx
    jne rp_idt
    lidt [lidt_opcode]
    ret
;end setup_idt fun

ignore_int: ;默认中断处理函数
    push ds
    push eax
    mov  eax,0x10
    mov  ds,ax
    mov  eax,69
    call write_char
    mov  al,0x20
    out  0x20,al
    pop eax
    pop ds
    iret
;BEGIN lidt

;Begin write_char
write_char:           ;打印字符函数
    push gs
    push ebx
    mov  ebx,VIDEO_DS ;取显存数据段
    mov  gs,bx
    mov  bx,[color]    ;取显示颜色
    cmp  bx,0
    jg   change_color ;当前显示1
    mov     ah, 0Ch ;黑底红字
    jmp  dis
change_color:
    mov  ah, 0Fh ;
dis:    
    mov  bx,[DWORD scr_loc] ;取显存位置
    mov  [byte gs:ebx],ax   ;写入显存
    add  ebx,2              ;下一个位置
    mov  [DWORD scr_loc],ebx ;放入内存保存
    cmp  ebx,2000            ;是否大于2000
    jb  exit                ;小于2000直接退出
    mov  bx,[color]
    cmp  bx,0          
    jz   change_color1   ;原来为0,改为1
    mov  bx,0            ;原来为1,改为0
    jmp  exit1
change_color1:
    mov  bx,1
exit1:
    mov  [color],bx
    xor  ebx,ebx               ;位置置0
    mov [DWORD scr_loc],ebx
exit:
    pop  ebx
    pop  gs
    ret
;End write_char
    
color:   db 0 ;显示颜色    
current: dd 0 ;当前执行的任务号
scr_loc: dw 0 ;屏幕位置

align 2                   
lidt_opcode:       ;LIDT寄存器加载的内容
    dw 256*8-1               ;长度
    dd idt                 ;线性基址
;END lidt    
lgdt_opcode:      ;GDT寄存器加载的内容
    dw (end_gdt - gdt)-1
    dd gdt

align 3           ;GDT内容
    gdt:
    dd 0x00000000 ;NULL
    dd 0x00000000
    dd 0x000007ff
    dd 0x00c09a00 ;内核代码段0x08
    dd 0x000007ff
    dd 0x00c09200 ;内核数据段0x10
    dd 0x80000002
    dd 0x00c0920b ;显存数据段描述符0x18
    dw 0x68
    dw tss0
    dw 0x8900
    dw 0x0000     ;内核TSS 0x20
end_gdt:

tss0:
    dd 0
    dd init_stack,0x10 ;内核esp0,ss0
    dd 0,0,0,0,0 ;esp1,ss1,esp2,ss2,cr3
    dd 0,0,0,0,0 ;eip,eflags,eax,ecx,edx
    dd 0,0,0,0,0 ;ebx,esp,ebp,esi,edi
    dd 0,0,0,0,0,0 ;es,cs,ss,ds,fs,gs
    dd 0,0x80000000 ;ldt,trace bitmap
    
idt:
    times 512 dd 0 ;初始化中断描述表
    
kernel_stack:
    times 128 dw 0    ;512B内核堆栈
init_stack:
    dd init_stack ;堆栈段偏移位置
    dw 0x10 ;内核数据段选择符

同样,将编译生成的bin文件复制到前面所说的软盘映像IMG的第2扇区开始处,引导程序就会将其加载到内存0处并执行,然后可以看到屏幕上不停地打印字符'E'.如果对程序中有所疑问,可以查看大家一起写操作系统预备知识.我会将中断的相关知识写到预备知识里。下一章将会介绍如何创建2个任务,并在任务间来回调度。

  



你可能感兴趣的:(大家一起写操作系统(3)-时钟中断)