• linux0.11学习笔记技术铺垫简单AB任务切换程序(4)向现存写数据并响应时钟中断


    上几节的介绍中,我们能够用bootloader加载32位代码,进入保护模式,并且跳转到了保护模式下的程序里,本篇我们实现在32位模式下完成写显存输出字符,并且在时钟中断中完成显示字符的程序。

    此后的代码,我们会略去bootloader不说,只说明32位程序head.s

    1. 看代码head.s

    SCRN_SEL = 0x18

    .globl startup_32
    .text
    startup_32:
        movl $0x10,%eax
        mov %ax,%ds
        mov %ax,%es
        mov %ax,%gs
        mov %ax,%fs

        lss init_stack,%esp

        call setup_idt
        call setup_gdt

        # init again
        movl $0x10,%eax
        mov %ax,%ds
        mov %ax,%es
        mov %ax,%gs
        mov %ax,%fs
        lss init_stack,%esp

    # setup timer & system call interrupt descriptors.
        movl $0x00080000, %eax   
        movw $timer_interrupt, %ax
        movw $0x8E00, %dx
        movl $0x08, %ecx
        lea idt(,%ecx,8), %esi
        movl %eax,(%esi)
        movl %edx,4(%esi)

        sti

    die:
        jmp die

    .align 2
    ignore_int:
        push %ds
        pushl %eax
        movl $0x10, %eax
        mov %ax, %ds
        mov $0x0c98, %ax            /* print 'C' */
        call write_char
        popl %eax
        pop %ds
        iret

    write_char:
        push %gs
        pushl %ebx

        mov $SCRN_SEL,%ebx
        mov %bx,%gs

        mov src_loc,%bx
        shl $1,%ebx
        mov %ax,%gs:(%ebx)
        shr $1,%ebx
        incl %ebx
        cmpl $2000,%ebx
        jb 1f
        movl $0,%ebx
    1:
        movl %ebx,src_loc

        popl %ebx
        pop %gs
        ret

    .align 2
    timer_interrupt:
        push %ds
        pushl %edx
        pushl %ecx
        pushl %ebx
        pushl %eax
        movl $0x10, %eax
        mov %ax, %ds
        movb $0x20, %al
        outb %al, $0x20

        mov $0x0c61,%ax
        call write_char

        popl %eax
        popl %ebx
        popl %ecx
        popl %edx
        pop %ds
        iret

    setup_idt:
        lea ignore_int,%edx
        movl $0x00080000,%eax
        movw %dx,%ax        /* selector = 0x0008 = cs */
        movw $0x8E00,%dx    /* interrupt gate - dpl=0, present */
        lea idt,%edi
        mov $256,%ecx
    rp_sidt:
        movl %eax,(%edi)
        movl %edx,4(%edi)
        addl $8,%edi
        dec %ecx
        jne rp_sidt
        lidt lidt_opcode
        ret

    setup_gdt:
        lgdt lgdt_opcode
        ret

    .align 2
    lidt_opcode:
        .word 256*8-1
        .long idt
    lgdt_opcode:
        .word (end_gdt-gdt)-1
        .long gdt

    src_loc:
        .long 0

    .align 2
    idt:
        .fill 256,8,0

    gdt:
        .quad 0x0000000000000000
        .quad 0x00c09a00000007ff
        .quad 0x00c09200000007ff
        .quad 0x00c0920b80000002
    end_gdt:

        .fill 128,4,0
    init_stack:
        .long init_stack
        .word 0x10

    2. 代码分析

    SCRN_SEL = 0x18

    .globl startup_32
    .text
    startup_32:
        movl $0x10,%eax
        mov %ax,%ds
        mov %ax,%es
        mov %ax,%gs
        mov %ax,%fs

        lss init_stack,%esp

    上边这些没啥好说的,设置环境,段寄存器和esp,为了后边的call指令和压栈等做准备。

        call setup_idt
        call setup_gdt

    上边调用函数,设置了gdt全局描述符和idt中断描述符,gdt是保护模式下必须的,因为cs存储的正是这个表中的描述符;

    如果要使用中断的话,idt也是必须的,中断服务程序需要idt指明。下面分析子程序。

    setup_idt:
        lea ignore_int,%edx
        movl $0x00080000,%eax
        movw %dx,%ax        /* selector = 0x0008 = cs */
        movw $0x8E00,%dx    /* interrupt gate - dpl=0, present */
        lea idt,%edi
        mov $256,%ecx
    rp_sidt:
        movl %eax,(%edi)
        movl %edx,4(%edi)
        addl $8,%edi
        dec %ecx
        jne rp_sidt
        lidt lidt_opcode
        ret

    以上代码大体意思是,把函数ignore_int这个默认中断服务的段和偏移信息,加载到256个中断描述符里,最后加载idt进寄存器。这样,系统所有的中断都会调用ignore_int这个函数来服务了。

    setup_gdt:
        lgdt lgdt_opcode
        ret

    直接加载gdt,没什么可说的。

    .align 2
    ignore_int:
        push %ds
        pushl %eax
        movl $0x10, %eax
        mov %ax, %ds
        mov $0x0c98, %ax            /* print 'C' */
        call write_char
        popl %eax
        pop %ds
        iret

    ignore_int子程序默认的中断服务程序,首先保护了需要用到ds和eax寄存器,设置了ds段地址为系统数据段地址,调用了写字符子程序,显示字符C.

    最后恢复现场,中断返回。

    write_char:
        push %gs
        pushl %ebx

        mov $SCRN_SEL,%ebx
        mov %bx,%gs

        mov src_loc,%bx
        shl $1,%ebx
        mov %ax,%gs:(%ebx)
        shr $1,%ebx
        incl %ebx
        cmpl $2000,%ebx
        jb 1f
        movl $0,%ebx
    1:
        movl %ebx,src_loc

        popl %ebx

        pop %gs
        ret

    以上代码,首先保护现场,之后去视频显存段选择符,再取当前输出位置src_loc,因为一个字符需要显存两个字节来形容(属性,值),所以要左移一位,相当于乘以2(shl $1,%ebx),之后把ax内容写进显存相应位置mov %ax,%gs:(%ebx)。同时保存了屏幕位置信息(movl %ebx,src_loc)。本函数实现了把参数ax中的内容,写进显存输出的功能。

    src_loc:
        .long 0

    .align 2
    idt:
        .fill 256,8,0

    gdt:
        .quad 0x0000000000000000
        .quad 0x00c09a00000007ff
        .quad 0x00c09200000007ff
        .quad 0x00c0920b80000002
    end_gdt:

        .fill 128,4,0
    init_stack:
        .long init_stack
        .word 0x10

    以上定义了系统用到的段和变量信息,不在详述。

        movl $0x00080000, %eax   
        movw $timer_interrupt, %ax
        movw $0x8E00, %dx
        movl $0x08, %ecx
        lea idt(,%ecx,8), %esi
        movl %eax,(%esi)
        movl %edx,4(%esi)

        sti

    以上代码实现了设置0x08号中断,即时钟中断的服务程序设置,跟ignore_int设置类型,填充段和服务函数偏移到idt描述符。

    最后一句打开了中断,系统可以响应中断了。

     

    3. 编译执行

    过程不再详述,可参考前边的文章。直接看结果截图:

     

    本文完.

  • 相关阅读:
    善待自己的恻隐之心
    FormLayout and FormData
    jquery获取元素索引值index()方法
    Kohana 之ORM文档篇
    css 圆角相框
    Kohana 之 request
    firefox通过XUL实现textoverflow:ellipsis的效果
    jquery 插件开发备注
    Kohana 之ORM实际使用篇
    PHP扩展编写与编译
  • 原文地址:https://www.cnblogs.com/linucos/p/2447027.html
Copyright © 2020-2023  润新知