6、分割编译与中断处理
2016-01-02 19:33
369 查看
分割源文件
bootpack.c
bootpack.h
graphic.c
dsctbl.c
Makefile
初始化PIC
PIC programmable interrupt controller 可编程中断控制器
int.c
中断程序的制作
鼠标是IRQ12,键盘是IRQ1,编写的相应中断处理程序为INT 0x2c和INT 0x21。
int.c节选
中断处理完成后,不能return,而必须执行IRETD指令,只能用汇编写。
naskfunc.nas节选
将中断函数_asm_inthandler21注册到IDT中,在dsctbl.c中的init_gdtidt中添加
bootpack.c节选
当按下键盘后,捕获了中断
总结一下中断的制作:
1、在asmhead.nas中添加一些设定,之前只是支持C语言导入,现在还要让它支持鼠标指针。
2、先初始化GDT、IDT,GDT为全局段号记录表,存放在GDTR寄存中,记录段的大小、段的起止位置,段的管理属性。IDT为中断记录表,记录了0~255号的中断号码与调用函数的对应关系。如果GDT还没有设置完就设置IDT会比较麻烦。
3、然后初始化PIC,PIC为可编程中断控制器,它将8个信号合成一个中断信号装置,监视着输入管脚的8个中断信号,只要有一个中断信号进来,就将唯一的输出管脚信号设成ON,并通知CPU。
4、编写中断处理程序,如inthandler21。
5、在naskfunc.nas中编写asm_inthandler,负责中断处理之前的处理,如对寄存的保存,调用中断处理程序,然后是中断之后的处理,返回。
6、将编写的中断处理程序注册到IDT中,如将asm_inthandler注册在IDT的第0x21号。
bootpack.c
#include "bootpack.h" #include "dsctbl.c" #include "graphic.c" #include <stdio.h> void HariMain(void) //程序从此处开始运行,函数名不能改 { struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0; char s[40], mcursor[256]; int mx, my; init_gdtidt(); //GDT和IDT的初始化 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(); } }
bootpack.h
/* asmhead.nas */ struct BOOTINFO { /* 0x0ff0-0x0fff */ char cyls; /* 启动读硬盘读到何处为止 */ char leds; /* 启动时键盘LED的状态 */ char vmode; /* 显卡模式为多少位颜色 */ char reserve; short scrnx, scrny; /* 画面分辨率 */ char *vram; }; #define ADR_BOOTINFO 0x00000ff0 /* naskfunc.nas */ void io_hlt(void); void io_cli(void); void io_out8(int port, int data); int io_load_eflags(void); void io_store_eflags(int eflags); void load_gdtr(int limit, int addr); void load_idtr(int limit, int addr); /* graphic.c */ void init_palette(void); void set_palette(int start, int end, unsigned char *rgb); void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1); void init_screen8(char *vram, int x, int y); void putfont8(char *vram, int xsize, int x, int y, char c, char *font); void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s); void init_mouse_cursor8(char *mouse, char bc); void putblock8_8(char *vram, int vxsize, int pxsize, int pysize, int px0, int py0, char *buf, int bxsize); //声明常数,表示哪种色号对应哪种颜色 #define COL8_000000 0 #define COL8_FF0000 1 #define COL8_00FF00 2 #define COL8_FFFF00 3 #define COL8_0000FF 4 #define COL8_FF00FF 5 #define COL8_00FFFF 6 #define COL8_FFFFFF 7 #define COL8_C6C6C6 8 #define COL8_840000 9 #define COL8_008400 10 #define COL8_848400 11 #define COL8_000084 12 #define COL8_840084 13 #define COL8_008484 14 #define COL8_848484 15 /* dsctbl.c */ struct SEGMENT_DESCRIPTOR { //8个字节 short limit_low, base_low; char base_mid, access_right; char limit_high, base_high; }; struct GATE_DESCRIPTOR { //8个字节 short offset_low, selector; char dw_count, access_right; short offset_high; }; void init_gdtidt(void); void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar); void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar); #define ADR_IDT 0x0026f800 #define LIMIT_IDT 0x000007ff #define ADR_GDT 0x00270000 #define LIMIT_GDT 0x0000ffff #define ADR_BOTPAK 0x00280000 #define LIMIT_BOTPAK 0x0007ffff #define AR_DATA32_RW 0x4092 #define AR_CODE32_ER 0x409a
graphic.c
//设置调色板 void init_palette(void) { /*声明了一个常数 table_rgb,相当于汇编中 table_rgb: RESB 48 static char 相当于汇编中的DB */ static unsigned char table_rgb[16 * 3] = { 0x00, 0x00, 0x00, /* 0:黑 */ 0xff, 0x00, 0x00, /* 1: 亮红 */ 0x00, 0xff, 0x00, /* 2: 亮绿 */ 0xff, 0xff, 0x00, /* 3: 亮黄 */ 0x00, 0x00, 0xff, /* 4: 亮蓝 */ 0xff, 0x00, 0xff, /* 5: 亮紫 */ 0x00, 0xff, 0xff, /* 6: 浅亮蓝 */ 0xff, 0xff, 0xff, /* 7: 白 */ 0xc6, 0xc6, 0xc6, /* 8: 亮灰*/ 0x84, 0x00, 0x00, /* 9: 暗红 */ 0x00, 0x84, 0x00, /* 10: 暗绿*/ 0x84, 0x84, 0x00, /* 11: 暗黄 */ 0x00, 0x00, 0x84, /* 12: 暗青 */ 0x84, 0x00, 0x84, /* 13: 暗紫 */ 0x00, 0x84, 0x84, /* 14: 浅暗蓝 */ 0x84, 0x84, 0x84 /* 15: 暗灰 */ }; set_palette(0, 15, table_rgb); return; } void set_palette(int start, int end, unsigned char *rgb) { int i, eflags; eflags = io_load_eflags(); /* 记录中断许可标志的值 */ io_cli(); /* 将中断许可标志置为0,禁止中断。访问调色板,首先屏蔽中断,如CLI */ io_out8(0x03c8, start); //将想要设定的调色板号码写入0x03c8 for (i = start; i <= end; i++) { //再将RGB顺序写入0x03c9 io_out8(0x03c9, rgb[0] / 4); io_out8(0x03c9, rgb[1] / 4); io_out8(0x03c9, rgb[2] / 4); rgb += 3; } io_store_eflags(eflags); /* 复原中断许可标志。如果最初执行了CLI,最后还要执行STI */ return; } //画面上有320*200个像素,左上为(0,0),右下为(319,319) //像素坐标(x, y)对应的VREAM地址为 0xa0000 + x + y * 320 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1) { int x, y; for (y = y0; y <= y1; y++) { for (x = x0; x <= x1; x++) vram[y * xsize + x] = c; } return; } void init_screen(char *vram, int x, int y) { boxfill8(vram, x, COL8_008484, 0, 0, x - 1, y - 29); boxfill8(vram, x, COL8_C6C6C6, 0, y - 28, x - 1, y - 28); boxfill8(vram, x, COL8_FFFFFF, 0, y - 27, x - 1, y - 27); boxfill8(vram, x, COL8_C6C6C6, 0, y - 26, x - 1, y - 1); boxfill8(vram, x, COL8_FFFFFF, 3, y - 24, 59, y - 24); boxfill8(vram, x, COL8_FFFFFF, 2, y - 24, 2, y - 4); boxfill8(vram, x, COL8_848484, 3, y - 4, 59, y - 4); boxfill8(vram, x, COL8_848484, 59, y - 23, 59, y - 5); boxfill8(vram, x, COL8_000000, 2, y - 3, 59, y - 3); boxfill8(vram, x, COL8_000000, 60, y - 24, 60, y - 3); boxfill8(vram, x, COL8_848484, x - 47, y - 24, x - 4, y - 24); boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y - 4); boxfill8(vram, x, COL8_FFFFFF, x - 47, y - 3, x - 4, y - 3); boxfill8(vram, x, COL8_FFFFFF, x - 3, y - 24, x - 3, y - 3); return; } void putfont8(char *vram, int xsize, int x, int y, char c, char *font) { int i; char *p, d; for (i = 0; i < 16; i++) //字符的存储:每行8位,共16行,所以一个字符用16个字节 { 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++) //字符串都是以0x00结尾的 { 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; } //进行显示,只需将buf中的数据复制到vram中 //vxsize与vysize与vram有关,vysize=0xa0000, vxsize=320 //pxsize与pysize是想要显示图像的大小,因为鼠标指针大小为16*16,所以两个值都为16 //buf指定图形存放地址,bxsize指定每一行含有的像素值,与pxsize大体相同 //px0与py0为要显示字符的起始像素点 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; }
dsctbl.c
void init_gdtidt(void) { //0x270000到0x27ffff设为GDT,因为这一块内存没有特殊用途 struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000; //0x26f800到0x26ffff设为IDT struct GATE_DESCRIPTOR *idt = (struct GATE_DESCRIPTOR *) 0x0026f800; int i; /* GDT的初始化*/ for (i = 0; i < 8192; i++) { //完成对8192个段的设定 set_segmdesc(gdt + i, 0, 0, 0); //将每个段的上限、基址、访问权限都设为0 } //将1号段上限设为4G,地址为0,表示CPU所能管理的全部内存本身 set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092); //将2号段上限设为512KB,地址为0x280000,正好是bootpack.hrb可以执行的地方 set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a); load_gdtr(0xffff, 0x00270000); //因为C语言不能给GDTR赋值,所以写成汇编 /* IDT的初始化 */ for (i = 0; i < 256; i++) { set_gatedesc(idt + i, 0, 0, 0); } load_idtr(0x7ff, 0x0026f800); //给IDTR赋值 return; } void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar) { if (limit > 0xfffff) { ar |= 0x8000; /* G_bit = 1 */ limit /= 0x1000; } sd->limit_low = limit & 0xffff; sd->base_low = base & 0xffff; sd->base_mid = (base >> 16) & 0xff; sd->access_right = ar & 0xff; sd->limit_high = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0); sd->base_high = (base >> 24) & 0xff; return; } void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar) { gd->offset_low = offset & 0xffff; gd->selector = selector; gd->dw_count = (ar >> 8) & 0xff; gd->access_right = ar & 0xff; gd->offset_high = (offset >> 16) & 0xffff; return; }
Makefile
OBJS_BOOTPACK = bootpack.obj naskfunc.obj hankaku.obj graphic.obj dsctbl.obj TOOLPATH = ../z_tools/ INCPATH = ../z_tools/haribote/ MAKE = $(TOOLPATH)make.exe -r NASK = $(TOOLPATH)nask.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.nas Makefile $(NASK) ipl.nas ipl.bin ipl.lst # 承接启动区,调用C语言 asmhead.bin : asmhead.nas Makefile $(NASK) asmhead.nas asmhead.bin asmhead.lst #使用makefont.exe编译hankaku.txt字库 hankaku.bin : hankaku.txt Makefile $(MAKEFONT) hankaku.txt hankaku.bin #然后连接所必须的接口信息,生成目标文件hankaku.obj,这样就可以与bootpack.obj连接了 hankaku.obj : hankaku.bin Makefile $(BIN2OBJ) hankaku.bin hankaku.obj _hankaku #使用obj2bim.exe将bootpack.obj生成二进制映像文件bootpack.bim, #这一步是因为C语言不能编写所有的程序,有一部分用汇编来写,然后链接到C语言程序上 bootpack.bim : bootpack.obj naskfunc.obj hankaku.obj Makefile $(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \ bootpack.obj naskfunc.obj hankaku.obj # 3MB+64KB=3136KB #使用bim2hrb.exe将bootpack.obj生成bootpack.hrb,这是因为映像文件只是将各部分全都链接在一起, #做成了完整的机器语言文件,为了实际使用,还需要加工,如加上识别的文件头,或者压缩等。 bootpack.hrb : bootpack.bim Makefile $(BIM2HRB) bootpack.bim bootpack.hrb 0 #核心OS二进制文件 haribote.sys : asmhead.bin bootpack.hrb Makefile copy /B asmhead.bin+bootpack.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 #利用一般规则进行简化 %.gas : %.c Makefile $(CC1) -o $*.gas $*.c %.nas : %.gas Makefile $(GAS2NASK) $*.gas $*.nas %.obj : %.nas Makefile $(NASK) $*.nas $*.obj $*.lst #生成 helloos.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) bootpack.nas -$(DEL) bootpack.map -$(DEL) bootpack.bim -$(DEL) bootpack.hrb -$(DEL) haribote.sys #把源程序以外的文件全部删干净 src_only : $(MAKE) clean -$(DEL) haribote.img
初始化PIC
PIC programmable interrupt controller 可编程中断控制器
int.c
/* PIC的初始化 */ void init_pic(void) { io_out8(PIC0_IMR, 0xff ); /* 禁止所有中断。IMR interrupt mask register 中断屏蔽寄存器 */ io_out8(PIC1_IMR, 0xff ); /* 禁止所有中断 */ //ICW 有4个,ICW1、ICW4与主板的配线方式、中断信号的电气特性有关,都为固定值,ICW3是与主-从连接的设置有关 io_out8(PIC0_ICW1, 0x11 ); /* 边沿触发模式(edge trigger mode)。ICW initial control word 初始化控制数据 */ io_out8(PIC0_ICW2, 0x20 ); /* IRQ0-7由INT20-27接收 */ io_out8(PIC0_ICW3, 1 << 2); /* PIC1由IRQ2连接. */ io_out8(PIC0_ICW4, 0x01 ); /* 无缓冲区模式 */ io_out8(PIC1_ICW1, 0x11 ); /* 边沿触发模式(edge trigger mode) */ io_out8(PIC1_ICW2, 0x28 ); /* IRQ8-15由INT28-2f接收 */ io_out8(PIC1_ICW3, 2 ); /* PIC1由IRQ2连接 */ io_out8(PIC1_ICW4, 0x01 ); /* 无缓冲区模式 */ io_out8(PIC0_IMR, 0xfb ); /* 11111011 PIC1以外全部禁止 */ io_out8(PIC1_IMR, 0xff ); /* 11111111 禁止所有中断 */ return; }
中断程序的制作
鼠标是IRQ12,键盘是IRQ1,编写的相应中断处理程序为INT 0x2c和INT 0x21。
int.c节选
/* 来自 PS/2 键盘的中断 */ void inthandler21(int *esp) //只是显示一条信息,然后保持在待机状态 { struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO; boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15); putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 21 (IRQ-1) : PS/2 keyboard"); for (;;) { io_hlt(); } } /* 来自PS/2鼠标的中断 */ void inthandler2c(int *esp) { struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO; boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15); putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 2C (IRQ-12) : PS/2 mouse"); for (;;) { io_hlt(); } }
中断处理完成后,不能return,而必须执行IRETD指令,只能用汇编写。
naskfunc.nas节选
GLOBAL _asm_inthandler21, _asm_inthandler2c EXTERN _inthandler21, _inthandler2c ;EXTERN表示在别的源文件中 _asm_inthandler21: PUSH ES PUSH DS PUSHAD ; 相当于PUSH EAX ECX EDX EBX ESP EBP ESI EDI MOV EAX,ESP PUSH EAX MOV AX,SS MOV DS,AX ; C语言认为DS ES SS都指同一个段 MOV ES,AX CALL _inthandler21 ; 调用别的文件中的函数 POP EAX POPAD POP DS POP ES IRETD ;中断返回 _asm_inthandler2c: PUSH ES PUSH DS PUSHAD MOV EAX,ESP PUSH EAX MOV AX,SS MOV DS,AX MOV ES,AX CALL _inthandler2c POP EAX POPAD POP DS POP ES IRETD
将中断函数_asm_inthandler21注册到IDT中,在dsctbl.c中的init_gdtidt中添加
/* IDT的设定 */ //asm_inthandle21注册在idt的第0x21号,2*8表示asm_inthandle21属于2号段,*8是因为低三位必须为0 //AR_INTGATE32将IDT的属性设为0x008e,这是这是用于中断处理的有效设定 set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32); set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);
bootpack.c节选
init_gdtidt(); //GDT和IDT的初始化 init_pic(); //PIC初始化 io_sti(); //仅执行STI命令,IF变为1,CPU接收来自外部设备的中断 //PIC属于外部设备,用IN OUT指令进行操作 io_out8(PIC0_IMR, 0xf9); /* PIC1(11111001) */ io_out8(PIC1_IMR, 0xef); /* (11101111) */
当按下键盘后,捕获了中断
总结一下中断的制作:
1、在asmhead.nas中添加一些设定,之前只是支持C语言导入,现在还要让它支持鼠标指针。
2、先初始化GDT、IDT,GDT为全局段号记录表,存放在GDTR寄存中,记录段的大小、段的起止位置,段的管理属性。IDT为中断记录表,记录了0~255号的中断号码与调用函数的对应关系。如果GDT还没有设置完就设置IDT会比较麻烦。
3、然后初始化PIC,PIC为可编程中断控制器,它将8个信号合成一个中断信号装置,监视着输入管脚的8个中断信号,只要有一个中断信号进来,就将唯一的输出管脚信号设成ON,并通知CPU。
4、编写中断处理程序,如inthandler21。
5、在naskfunc.nas中编写asm_inthandler,负责中断处理之前的处理,如对寄存的保存,调用中断处理程序,然后是中断之后的处理,返回。
6、将编写的中断处理程序注册到IDT中,如将asm_inthandler注册在IDT的第0x21号。
相关文章推荐
- Node.js的REPL环境
- 简单使用URLConnection、HttpURLConnection和HttpClient访问网络资源
- leetcode之Spiral Matrix
- 使用 ContentProvider 管理联系人(包括每人有多个电话号码)
- 手机屏幕材料区别 TFT和OLED
- Python中ConfigParser模块应用
- Tkinter 8.5 参考手册: a GUI for Python (五)11 窗口的名称
- Tkinter 8.5 参考手册: a GUI for Python (五)9图像
- Tkinter 8.5 参考手册: a GUI for Python (五)8(鼠标)指针
- Tkinter 8.5 参考手册: a GUI for Python (五)6
- Tkinter 8.5 参考手册: a GUI for Python (五)5
- Tkinter 8.5 参考手册: a GUI for Python (五)4
- Tkinter 8.5 参考手册: a GUI for Python (五)3
- Tkinter 8.5 参考手册: a GUI for Python (五)2
- Tkinter 8.5 参考手册: a GUI for Python (五)1
- Tkinter 8.5 参考手册: a GUI for Python (五)
- 联想因为Lenovo Service Engine BIOS再一次陷入麻烦
- 用java解 leetcode上easy题2(开始探险)
- 【bzoj1433】[ZJOI2009]假期的宿舍
- 【tyvj1033】悠闲的漫步