《30天自制操作系统》笔记五六
来源:互联网 发布:注册码万能破解软件 编辑:程序博客网 时间:2024/04/27 15:45
书上第五六天所涉及的内容主要是GDT、IDT以及PIC。这两部分我就合在一起写好了。(主要的原因是做完第五天的以后看到代码乱得惨不忍睹,就自己开始整理了一下,随后又发现第六天就是讲分割源文件的,所以就继续看了下去。)
第五天一上来,作者就介绍了结构体。于是,出现了struct boot_info,struct seg_desc,以及struct gate_desc三者。(命名我是按照自己的习惯来改动的。)
C语言语法上的东西就不提了。
有点意思的是,继之前恶心简洁的图形化界面之后,我们这次来做文字显示。
字体怎么来呢?最简单的一种方法就是按照之前做图像的时候的方法来做,也就是用boxfill8(),但是这样也太不专业了吧= =
我们有一种好一点的方法,就是重写一个putfont8()函数,用来做字符显示。(ps:后来我改名为print_char()了)直接用作者给的hankaku.txt来导入字体。(其实这个字体包也没有多高端嘛……要用到新的工具makefont.exe。可以从hankaku.txt得到hankaku.bin。之后我们再用bin2obj.exe来将其转化为hankaku.obj文件。同时,对应的修改Makefile。
值得注意的是:一定要加上"$BIN2OBJ ......"的说明!当时就是因为抄漏而导致了大量的失败。
而显示变量名按照上面的来做基本也没问题。不过不知道为什么,到后面我多加"#include "bootpack.h""语句之后,直接就只声明“char s[40]”是不够的,要初始化!可以是"char s[40]={'0'}"。如果没有初始化,就会发现那个字符就是显示不出来,然后你还以为是print_str或者print_char的问题,对照源代码N次!!!(TAT)
再来是鼠标的指针,这个其实还是蛮无聊有趣的。为了凸显我跟作者的不一样,我把指针弄成了8x8大小的,结果……其实也还是能用的,而且大小我(我不是处女座……)也刚好合适。
文件分割不难,略过。也就是多出了graphic.c,dsctbl.c,以及后面的int.c。
有一点是新学习到的,那就是Makefile的一般规则:
# general rules for *.gas, *.nas and *.obj%.gas : %.c Makefile$(CC1) -o $*.gas $*.c%.nas : %.gas Makefile$(GAS2NASK) $*.gas $*.nas%.obj : %.nas Makefile$(NASK) $*.nas $*.obj $*.lst关于Makefile,有个很详细的教程:http://bbs.chinaunix.net/thread-408225-1-1.html
之后得找时间认真学习一下才行。
本次学习遇到的第一个重难点在于GDT以及IDT。
其实作者的文笔还是挺好的,写得很顺畅。(虽然到后来发现他有些地方机智的绕掉了……)
该记住的有:
GDT大小为2^13*(8B) = 64KB
/* segment descriptor, 8B *//* base : base_low(2B), base_mid(1B) and base_high(1B) * limit : limit_low(1B) and limit_high(1B) * access_right: the highest bit is Gbit, when Gbit==1, the unit of limit is PAGE; the highest 4bits are put into the highest 4bits of limit_high. thus, to program, access_right is xxxx0000xxxxxxxx; the highest 4bits are "GD00"(used after 386), G is Gbit, D means 32-mode or 16-mode. lowest 8bits: 0x00: unused descriptor table 0x92: for system. RW-. 0x9a: for system. R-X. 0xf2: for applications. RW-. 0xfa: for applications. R-X. */struct seg_desc {short limit_low;short base_low;char base_mid; char access_right;char limit_high; char base_high;};而后面的gate_desc,类比一下就好。
set_seg_desc,set_gate_desc,init_gdt_idt就慢慢看代码吧。
C不能直接给GDTR赋值,所以要用load_gdtr,在naskfunc.nas中:
_load_gdtr:; void load_gdtr(int limit, int addr);; "MOV [ESP+6] [ESP+4]"MOVAX, [ESP+4]; limitMOV[ESP+6], AXLGDT[ESP+6]RET看起来,这段函数似乎做的就是"MOV [ESP+6] [ESP+4]"但是为什么要这么做呢?为什么不直接"LGDT [ESP+4]"呢?
这其中可是大有奥秘的。
首先,GDTR的低16位是段上限,高32位是地址。我们不能直接用MOV来赋值,而只能直接指定一个内存地址,让它去读这48位。想想看,假设我们传的段上限是0x0000ffff,而地址是00270000(事实上我们用的也就是这个)。这时候,地址从ESP+4往高处走是:【FF FF 00 00 00 00 27 00】。但我们希望给GDTR的是这样一部分:【FF FF 00 00 27 00】那可以看到,作者在这里把ESP+4赋值到ESP+6,然后就变成了【FF FF FF FF 00 00 27 00】,只要直接从ESP+6开始读就可以了。
哎,在这里实在不得不感叹一声作者太神了!事实上如果你传参数的时候,两个参数的位置如果换一下的话,你会发现很不好处理!(我一开始是想这么干来着。)
set_seg_desc太高深,跳过。(作者也没讲多少)
然后就到PIC。一到硬件就各种蛋疼哎!
PIC指的是Programmable interrupt controller。
然后看图:
用图比较容易理解。
简而言之,PIC监视着输入管脚的8个中断信号,只要有一个中断信号进来,就将唯一的输出管脚信号变成ON,并通知CPU。
然后下面是一段咋看之下很不明觉厉的代码:
void init_pic(void);#define PIC0_ICW10x0020#define PIC0_OCW20x0020#define PIC0_IMR0x0021#define PIC0_ICW20x0021#define PIC0_ICW30x0021#define PIC0_ICW40x0021#define PIC1_ICW10x00a0#define PIC1_OCW20x00a0#define PIC1_IMR0x00a1#define PIC1_ICW20x00a1#define PIC1_ICW30x00a1#define PIC1_ICW40x00a1
/* Initialization of pic */void init_pic (void) {io_out8(PIC0_IMR, 0xff ); /* disable all interrupts */io_out8(PIC1_IMR, 0xff ); /* disable all interrupts */io_out8(PIC0_ICW1, 0x11 ); /* edge trigger mode*/io_out8(PIC0_ICW2, 0x20 ); /* IRQ-7 is received by INT20-27 */io_out8(PIC0_ICW3, 1 << 2); /* PIC1 is connected by IRQ2*/io_out8(PIC0_ICW4, 0x01 ); /* no-buffer mode */io_out8(PIC1_ICW1, 0x11 ); /* edge trigger mode */io_out8(PIC1_ICW2, 0x28 ); /* IRQ-15 is received by INT28-2f */io_out8(PIC1_ICW3, 2 ); /* PIC1 is connected by IRQ2 */io_out8(PIC1_ICW4, 0x01 ); /* no-buffer mode */io_out8(PIC0_IMR, 0xfb ); /* 11111011 disable all except PIC1 */io_out8(PIC1_IMR, 0xff ); /* 11111111 disable all interrupt */}简单的翻译一下,IMR指的是interrupt mask register,ICW指的是"initial control word",都是8位寄存器。
IMR中8位分别对应8个IRQ信号,如果某一位为1,则该为对应的信号被屏蔽,PIC就忽略之。
ICW不一定是16位,因硬件而不同。有4个,分别编号1~4,共有4个字节的数据。
ICW1和4与硬件有关,忽略之。
ICW3是有关主从连接的设定,对主PIC而言,第几号IRQ与从PIC相连,是用8位来决定的。如果把这些为全部设为1,则主PIC能驱动8个从PIC。
不过呢……这个是硬件决定的,我们也无力。
所以只能改ICW2咯。
ICW2决定了IRQ以哪一号中断通知CPU。通过PIC用数据信号线传送给CPU“0xcd 0x??”来实现的。这里的0xcd实际上就是调用BIOS时用的INT指令。
这次以INT0x20~0x2f接收中断信号IRQ0~15而设定的。
开始看程序,注意鼠标时IRQ12, 键盘是IRQ1。
void inthandler21(int *esp)/* interrupt from PS/2 keyboard */{struct boot_info *binfo = (struct boot_info *) BOOT_ADDR;boxfill8(binfo->vram, binfo->scrnx, BLACK, 0, 0, 32 * 8 - 1, 15);print_str(binfo->vram, binfo->scrnx, 0, 0, WHITE, "INT 21 (IRQ-1) : PS/2 keyboard");for (;;) {io_hlt();}}完了之后还得让它执行IRETD:
_asm_inthandler21:PUSHESPUSHDSPUSHADMOVEAX,ESPPUSHEAXMOVAX,SSMOVDS,AXMOVES,AXCALL_inthandler21POPEAXPOPADPOPDSPOPESIRETD
关于栈,不解释。
这些差不多就是今天全部的内容了。(唉,累……)
- 《30天自制操作系统》笔记五六
- 30天自制操作系统笔记(五六)
- 《30天自制操作系统》学习笔记(六)
- 《30天自制操作系统》学习笔记(五)
- 《30天自制操作系统》学习笔记_helloos0
- 30天自制操作系统笔记(一二)
- 30天自制操作系统笔记(三)
- 30天自制操作系统笔记(四)
- 30天自制操作系统笔记(七八)
- 30天自制操作系统笔记(九十)
- 《30天自制操作系统》笔记一二
- 《30天自制操作系统》笔记三
- 《30天自制操作系统》笔记三
- 《30天自制操作系统》笔记四
- 《30天自制操作系统》学习笔记
- 30天自制操作系统学习笔记
- 《30天自制操作系统》学习笔记Day1
- 《30天自制操作系统》学习笔记Day2
- jvm类加载过程分析
- git merge 和 git rebase 区别
- leetcode 第9题 Palindrome Number
- MySpring dataSource从配置文件获取
- apktool反编绎工具体验
- 《30天自制操作系统》笔记五六
- Git 常用命令
- 有经验的Java开发者和架构师容易犯的10个错误(上)
- Android小应用——监控屏幕使用时间
- Linux防火墙
- 【语言-C++】MFC 输出中文文本
- Webstorm
- 数据结构基础(二)队列的实现
- 有经验的Java开发者和架构师容易犯的10个错误(下)