首先就是对前一天程序的一些优化,引入了结构体:
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我已经非常透彻理解了保护模式寻址,画了下面的图:
; 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