自制操作系统(五) 保护模式寻址原理、字符鼠标指针显示
2016-04-21 22:27
239 查看
首先就是对前一天程序的一些优化,引入了结构体:
这里面用到的结构体BOOTINFO其实只是一个存储单元。
binfo = (struct BOOTINFO *) 0x0ff0;这句获取0x0ff0开始一连串值。
作用相当于binfo_scrnx = (short *)0xff4;等几句。
另外,作为参数时,可以不用传入那么多参数。
之后就是,字符显示:
这段代码显示字符A,具体原理就是用for语句将8个像素的程序循环16遍。需要在HariMain里,
初始化
static char font_A[16] = {
0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
};
作为参数。
一个A就出现这么多代码,肯定不行。所以作者引入了OSASK字体。加入hankaku.txt文件。另需编译器makefont.exe将之编译为hankaku.bin文件。
新的Makefile:
之后有了显示字符和字符串的函数:
鼠标的显示就是一样的道理了:
此时c_main.c里main函数如下:
可见,先初始化调色板、再初始化屏幕、再初始化鼠标,然后画鼠标,显示字符。
这章涉及保护模式寻址,关于GDT。
2016.06.06我已经非常透彻理解了保护模式寻址,画了下面的图:
![](https://images2015.cnblogs.com/blog/726249/201606/726249-20160606231911199-1057278622.png)
虽然人们在谈到中断(Interrupt)时,总会拿轮询(Polling)来做“反面”例子,但中断和轮询并不是完全对立的两个概念,呃,它们是对立统一的。
“CPU执行完每条指令时,都会去检查一个中断标志位”,这句话是所有关于中断长篇大论的开场白,但很容易被人忽略,其实,这就是中断的本质。
举个例子,CPU老板是一家公司的光杆司令,所有的顾客都要他亲自跑去处理,还要跟有关部门打点关系,CPU觉得顾客和公关这两样事它一个人搞不来,这就是轮询;终于这家公司升级发展了,CPU老板请了一个秘书,所有的顾客都先由秘书经手,CPU心情好的时候就去看一下,大部分时间都忙着去公关了,这时它觉得轻松了很多,这就是中断了~~
也就是说,中断和轮询是从CPU老板的角度来看的,不管怎样,事件都还是有人来时刻跟踪才能被捕获处理,不过是老板还是秘书的问题。所有的中断(或者异步,回调等)背后都有一个轮询(循环,listener)。
通往32位之路:
PIC关闭一切中断
设置A20
切换到保护模式。
INSTRSET指令,是为了能够使用386以后的LGDT,EAX,CR0等关键字。
通过带入CR0而切换到保护模式时,要马上执行JMP指令。因为变成保护模式后,机器语言的解释要发生变化。CPU为了加快指令的执行速度而使用了管道这一机制,就是说,前一条指令还在执行的时候,就开始解释下一条甚至是再下一条指令。因为模式变了,就得重新解释一遍,所以加入了JMP指令
struct BOOTINFO { char cyls, leds, vmode, reserve; short scrnx, scrny; char *vram; }; void HariMain(void) { char *vram; int xsize, ysize; struct BOOTINFO *binfo; init_palette(); binfo = (struct BOOTINFO *) 0x0ff0; xsize = (*binfo).scrnx; ysize = (*binfo).scrny; vram = (*binfo).vram; init_screen(vram, xsize, ysize); for (;;) { io_hlt(); } }
这里面用到的结构体BOOTINFO其实只是一个存储单元。
binfo = (struct BOOTINFO *) 0x0ff0;这句获取0x0ff0开始一连串值。
作用相当于binfo_scrnx = (short *)0xff4;等几句。
另外,作为参数时,可以不用传入那么多参数。
之后就是,字符显示:
void putfont8(char *vram, int xsize, int x, int y, char c, char *font) { int i; char *p, d /* data */; for (i = 0; i < 16; i++) { p = vram + (y + i) * xsize + x; d = font[i]; if ((d & 0x80) != 0) { p[0] = c; } if ((d & 0x40) != 0) { p[1] = c; } if ((d & 0x20) != 0) { p[2] = c; } if ((d & 0x10) != 0) { p[3] = c; } if ((d & 0x08) != 0) { p[4] = c; } if ((d & 0x04) != 0) { p[5] = c; } if ((d & 0x02) != 0) { p[6] = c; } if ((d & 0x01) != 0) { p[7] = c; } } return; }
这段代码显示字符A,具体原理就是用for语句将8个像素的程序循环16遍。需要在HariMain里,
初始化
static char font_A[16] = {
0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
};
作为参数。
一个A就出现这么多代码,肯定不行。所以作者引入了OSASK字体。加入hankaku.txt文件。另需编译器makefont.exe将之编译为hankaku.bin文件。
新的Makefile:
TOOLPATH = ../z_tools/ INCPATH = ../z_tools/haribote/ MAKE = $(TOOLPATH)make.exe -r NASK = $(TOOLPATH)nask.exe NASM = $(TOOLPATH)nasm.exe CC1 = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet GAS2NASK = $(TOOLPATH)gas2nask.exe -a OBJ2BIM = $(TOOLPATH)obj2bim.exe MAKEFONT = $(TOOLPATH)makefont.exe BIN2OBJ = $(TOOLPATH)bin2obj.exe BIM2HRB = $(TOOLPATH)bim2hrb.exe RULEFILE = $(TOOLPATH)haribote/haribote.rul EDIMG = $(TOOLPATH)edimg.exe IMGTOL = $(TOOLPATH)imgtol.com COPY = copy DEL = del default : $(MAKE) img ipl.bin : ipl.asm Makefile $(NASM) ipl.asm -o ipl.bin main.bin : main.nas Makefile $(NASK) main.nas main.bin main.lst c_main.gas : c_main.c Makefile $(CC1) -o c_main.gas c_main.c c_main.nas : c_main.gas Makefile $(GAS2NASK) c_main.gas c_main.nas c_main.obj : c_main.nas Makefile $(NASK) c_main.nas c_main.obj c_main.lst assemblyFunc.obj : assemblyFunc.nas Makefile $(NASK) assemblyFunc.nas assemblyFunc.obj assemblyFunc.lst hankaku.bin : hankaku.txt Makefile $(MAKEFONT) hankaku.txt hankaku.bin hankaku.obj : hankaku.bin Makefile $(BIN2OBJ) hankaku.bin hankaku.obj _hankaku c_main.bim : c_main.obj assemblyFunc.obj hankaku.obj Makefile $(OBJ2BIM) @$(RULEFILE) out:c_main.bim stack:3136k map:c_main.map \ c_main.obj assemblyFunc.obj hankaku.obj # 3MB+64KB=3136KB c_main.hrb : c_main.bim Makefile $(BIM2HRB) c_main.bim c_main.hrb 0 haribote.sys : main.bin c_main.hrb Makefile copy /B main.bin+c_main.hrb haribote.sys haribote.img : ipl.bin haribote.sys Makefile $(EDIMG) imgin:../z_tools/fdimg0at.tek \ wbinimg src:ipl.bin len:512 from:0 to:0 \ copy from:haribote.sys to:@: \ imgout:haribote.img img : $(MAKE) haribote.img run : $(MAKE) img $(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin $(MAKE) -C ../z_tools/qemu install : $(MAKE) img $(IMGTOL) w a: haribote.img clean : -$(DEL) *.bin -$(DEL) *.lst -$(DEL) *.gas -$(DEL) *.obj -$(DEL) c_main.nas -$(DEL) c_main.map -$(DEL) c_main.bim -$(DEL) c_main.hrb -$(DEL) haribote.sys src_only : $(MAKE) clean -$(DEL) haribote.img
之后有了显示字符和字符串的函数:
void putfont8(char *vram, int xsize, int x, int y, char c, char *font) { int i; char *p, d /* data */; for (i = 0; i < 16; i++) { p = vram + (y + i) * xsize + x; d = font[i]; if ((d & 0x80) != 0) { p[0] = c; } if ((d & 0x40) != 0) { p[1] = c; } if ((d & 0x20) != 0) { p[2] = c; } if ((d & 0x10) != 0) { p[3] = c; } if ((d & 0x08) != 0) { p[4] = c; } if ((d & 0x04) != 0) { p[5] = c; } if ((d & 0x02) != 0) { p[6] = c; } if ((d & 0x01) != 0) { p[7] = c; } } return; }
void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
extern char hankaku[4096];
for (; *s != 0x00; s++) {
putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
x += 8;
}
return;
}
鼠标的显示就是一样的道理了:
void init_mouse_cursor8(char *mouse, char bc) /* 准备鼠标指针 */ { static char cursor[16][16] = { "**************..", "*OOOOOOOOOOO*...", "*OOOOOOOOOO*....", "*OOOOOOOOO*.....", "*OOOOOOOO*......", "*OOOOOOO*.......", "*OOOOOOO*.......", "*OOOOOOOO*......", "*OOOO**OOO*.....", "*OOO*..*OOO*....", "*OO*....*OOO*...", "*O*......*OOO*..", "**........*OOO*.", "*..........*OOO*", "............*OO*", ".............***" }; int x, y; for (y = 0; y < 16; y++) { for (x = 0; x < 16; x++) { if (cursor[y][x] == '*') { mouse[y * 16 + x] = COL8_000000; } if (cursor[y][x] == 'O') { mouse[y * 16 + x] = COL8_FFFFFF; } if (cursor[y][x] == '.') { mouse[y * 16 + x] = bc; } } } return; } void putblock8_8(char *vram, int vxsize, int pxsize, int pysize, int px0, int py0, char *buf, int bxsize) { int x, y; for (y = 0; y < pysize; y++) { for (x = 0; x < pxsize; x++) { vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x]; } } return; }
此时c_main.c里main函数如下:
void HariMain(void) { struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0; char s[40], mcursor[256]; int mx, my; init_palette(); init_screen(binfo->vram, binfo->scrnx, binfo->scrny); mx = (binfo->scrnx - 16) / 2; my = (binfo->scrny - 28 - 16) / 2; init_mouse_cursor8(mcursor, COL8_008484); putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16); sprintf(s, "(%d, %d)", mx, my); putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); for (;;) { io_hlt(); } }
可见,先初始化调色板、再初始化屏幕、再初始化鼠标,然后画鼠标,显示字符。
这章涉及保护模式寻址,关于GDT。
2016.06.06我已经非常透彻理解了保护模式寻址,画了下面的图:
![](https://images2015.cnblogs.com/blog/726249/201606/726249-20160606231911199-1057278622.png)
虽然人们在谈到中断(Interrupt)时,总会拿轮询(Polling)来做“反面”例子,但中断和轮询并不是完全对立的两个概念,呃,它们是对立统一的。
“CPU执行完每条指令时,都会去检查一个中断标志位”,这句话是所有关于中断长篇大论的开场白,但很容易被人忽略,其实,这就是中断的本质。
举个例子,CPU老板是一家公司的光杆司令,所有的顾客都要他亲自跑去处理,还要跟有关部门打点关系,CPU觉得顾客和公关这两样事它一个人搞不来,这就是轮询;终于这家公司升级发展了,CPU老板请了一个秘书,所有的顾客都先由秘书经手,CPU心情好的时候就去看一下,大部分时间都忙着去公关了,这时它觉得轻松了很多,这就是中断了~~
也就是说,中断和轮询是从CPU老板的角度来看的,不管怎样,事件都还是有人来时刻跟踪才能被捕获处理,不过是老板还是秘书的问题。所有的中断(或者异步,回调等)背后都有一个轮询(循环,listener)。
通往32位之路:
PIC关闭一切中断
; PIC关闭一切中断 ; 根据AT兼容机的规格、初始化PIC ; 必须在CLI之前进行,到CLI挂起 ; 随后进行PIC初始化 MOV AL,0xff OUT 0x21,AL NOP ; 如果连续进行OUT命令,有些机种不可以 OUT 0xa1,AL CLI ; 禁止CPU级别中断
设置A20
; OPEN A20GATE CALL waitkbdout MOV AL,0xd1 OUT 0x64,AL CALL waitkbdout MOV AL,0xdf ; enable A20 OUT 0x60,AL CALL waitkbdout
切换到保护模式。
INSTRSET指令,是为了能够使用386以后的LGDT,EAX,CR0等关键字。
通过带入CR0而切换到保护模式时,要马上执行JMP指令。因为变成保护模式后,机器语言的解释要发生变化。CPU为了加快指令的执行速度而使用了管道这一机制,就是说,前一条指令还在执行的时候,就开始解释下一条甚至是再下一条指令。因为模式变了,就得重新解释一遍,所以加入了JMP指令
; 保护模式 [INSTRSET "i486p"] ; 开始使用486命令 LGDT [GDTR0] ; 临时GDT MOV EAX,CR0 AND EAX,0x7fffffff ; bit31设0,禁止分页 OR EAX,0x00000001 ; bit0设1,为了切换到保护模式 MOV CR0,EAX JMP pipelineflush pipelineflush: MOV AX,1*8 ; 可读写的段,32bit MOV DS,AX MOV ES,AX MOV FS,AX MOV GS,AX MOV SS,AX
相关文章推荐
- 每日工作总结03
- Python排序算法之冒泡排序
- ORA-12704:字符集不匹配
- 小甲鱼PE详解之输入表(导出表)详解(PE详解09)
- 像素翻转之程序员面试经典
- 深度理解链式前向星
- hibernate的分页查询
- OpenCv学习笔记--支持向量机SVM之C++的实现(1)
- Java中的Clone机制(浅层复制)
- 第一次用OpenGL遇到的几个小问题
- android设计模式的使用之适配器模式
- 上课笔记分享-1-第17课时-1-多域名-统一沟通-环境
- 上课笔记分享-1-第17课时-1-多域名-统一沟通-环境
- HDU 1406 废了点时间(人本了),水过
- Python IO编程——学习笔记
- SAR成像基础知识急救箱(一)卷积 相关 滤波器那些事儿
- Spark Shuffle FetchFailedException解决方案
- shell排序
- 快速排序
- java——图形用户界面编程-——布局