linux0.11学习笔记-技术铺垫-简单AB任务切换程序(4)-向现存写数据并响应时钟中断
来源:互联网 发布:快手 知乎 编辑:程序博客网 时间:2024/06/07 16:51
转自:http://www.cnblogs.com/linucos/archive/2012/04/14/2447027.html
上几节的介绍中,我们能够用bootloader加载32位代码,进入保护模式,并且跳转到了保护模式下的程序里,本篇我们实现在32位模式下完成写显存输出字符,并且在时钟中断中完成显示字符的程序。
此后的代码,我们会略去bootloader不说,只说明32位程序head.s
1. 看代码head.s
SCRN_SEL = 0x18.globl startup_32.textstartup_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) stidie: jmp die.align 2ignore_int: push %ds pushl %eax movl $0x10, %eax mov %ax, %ds mov $0x0c98, %ax /* print 'C' */ call write_char popl %eax pop %ds iretwrite_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,%ebx1: movl %ebx,src_loc popl %ebx pop %gs ret.align 2timer_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 iretsetup_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,%ecxrp_sidt: movl %eax,(%edi) movl %edx,4(%edi) addl $8,%edi dec %ecx jne rp_sidt lidt lidt_opcode retsetup_gdt: lgdt lgdt_opcode ret.align 2lidt_opcode: .word 256*8-1 .long idtlgdt_opcode: .word (end_gdt-gdt)-1 .long gdtsrc_loc: .long 0.align 2idt: .fill 256,8,0gdt: .quad 0x0000000000000000 .quad 0x00c09a00000007ff .quad 0x00c09200000007ff .quad 0x00c0920b80000002end_gdt: .fill 128,4,0init_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. 编译执行
过程不再详述,可参考前边的文章。直接看结果截图:
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. 编译执行
过程不再详述,可参考前边的文章。直接看结果截图:
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(4)-向现存写数据并响应时钟中断
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(5)-实现三个任务切换
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(3)-调试手段和方法
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(1)-实现一个简单的bootloader
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(2)-可加载执行其他程序的bootloader
- linux0.11任务切换switch_to
- linux0.11 汇编 切换到任务0 的相关描述
- linux0.11 进程切换
- 大家一起写操作系统(4)-简单的任务切换
- Linux0.11 引导程序Boot学习
- Linux0.12任务调度与进程切换
- 时钟中断技术
- linux0.11学习笔记(1)
- linux0.11学习笔记(2)
- 学习笔记之---简单时钟
- linux0.11内核中断处理
- linux0.11 中断和异常
- 时钟中断处理程序
- VC 绘图,使用双缓冲技术实现
- ubuntu12.04安装JDK
- C语言中printf输出的总结
- window.open 全屏,js去掉工具栏,菜单栏,地址栏,状态栏..
- SQL中的表 与关系数据库
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(4)-向现存写数据并响应时钟中断
- 怎样花两年时间去面试一个人
- 快速问医生:不用挂号就能看医生
- UIWindow与视图UIView深层解析
- 什么是Drupal CCK?
- java调用oracle数据库的分页存储过程
- HDOJ 1232 畅通工程
- make oldconfig
- linux内核的水很深,分享一下关于__raw_writel()出错的问题