ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Day6 ——自制操作系统

2022-10-16 19:50:17  阅读:308  来源: 互联网

标签:Day6 自制 操作系统


分割源文件

  • 如果graphic.c也想使用naskfunc.nas的函数,就必须要写上“void io_out8(int port, int data); ”这种函数声明。虽然这都已经写在bootpack.c里了,但编译器在编译graphic.c时,根本不知道有bootpack.c存在
修改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 ipl10.bin : ipl10.nas Makefile $(NASK) ipl10.nas ipl10.bin ipl10.lst asmhead.bin : asmhead.nas Makefile $(NASK) asmhead.nas asmhead.bin asmhead.lst hankaku.bin : hankaku.txt Makefile $(MAKEFONT) hankaku.txt hankaku.bin hankaku.obj : hankaku.bin Makefile $(BIN2OBJ) hankaku.bin hankaku.obj _hankaku bootpack.bim : $(OBJS_BOOTPACK) Makefile $(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map $(OBJS_BOOTPACK) # 3MB+64KB=3136KB bootpack.hrb : bootpack.bim Makefile $(BIM2HRB) bootpack.bim bootpack.hrb 0 haribote.sys : asmhead.bin bootpack.hrb Makefile copy /B asmhead.bin+bootpack.hrb haribote.sys haribote.img : ipl10.bin haribote.sys Makefile $(EDIMG) imgin:../z_tools/fdimg0at.tek wbinimg src:ipl10.bin len:512 from:0 to:0 copy from:haribote.sys to:@: imgout:haribote.img # make.exe会首先寻找普通的生成规则,如果没找到,就尝试用一般规则。 # 所以,即使一般规则和普通生成规则有冲突,也不会有问题 %.gas : %.c Makefile $(CC1) -o $*.gas $*.c %.nas : %.gas Makefile $(GAS2NASK) $*.gas $*.nas %.obj : %.nas Makefile $(NASK) $*.nas $*.obj $*.lst # 僐儅儞僪 img : $(MAKE) haribote.img run : $(MAKE) img $(COPY) haribote.img ..z_toolsqemufdimage0.bin $(MAKE) -C ../z_tools/qemu install : $(MAKE) img $(IMGTOL) w a: haribote.img clean : -$(DEL) *.bin -$(DEL) *.lst -$(DEL) *.obj -$(DEL) bootpack.map -$(DEL) bootpack.bim -$(DEL) bootpack.hrb -$(DEL) haribote.sys src_only : $(MAKE) clean -$(DEL) haribote.img

整理源文件

  • 新建bootpack.h,将头部放进去 仅由函数声明和#define等组成的文件,我们称之为头文件 /* 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 { short limit_low, base_low; char base_mid, access_right; char limit_high, base_high; }; struct GATE_DESCRIPTOR { 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文件 有关图像的处理 引入bootpack.h文件:#include "bootpack.h" 编译器见到了这一行,就将该行替换成所指定文件的内容,然后进行编译。所以,写在“bootpack.h”里的所有内容,也都间接地写到了“graphic.c”中. #include "bootpack.h" void init_palette(void) { 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; /* static char 柦椷偼丄僨乕僞偵偟偐巊偊側偄偗偳DB柦椷憡摉 */ } void set_palette(int start, int end, unsigned char *rgb) { int i, eflags; eflags = io_load_eflags(); /* 妱傝崬傒嫋壜僼儔僌偺抣傪婰榐偡傞 */ io_cli(); /* 嫋壜僼儔僌傪0偵偟偰妱傝崬傒嬛巭偵偡傞 */ io_out8(0x03c8, start); for (i = start; i <= end; i++) { io_out8(0x03c9, rgb[0] / 4); io_out8(0x03c9, rgb[1] / 4); io_out8(0x03c9, rgb[2] / 4); rgb += 3; } io_store_eflags(eflags); /* 妱傝崬傒嫋壜僼儔僌傪尦偵栠偡 */ return; } 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_screen8(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 /* 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) /* 儅僂僗僇乕僜儖傪弨旛乮16x16乯 */ { 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; } dsctbl.c 文件 关于GDT、IDT等descriptor table的处理 #include "bootpack.h" void init_gdtidt(void) { struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT; struct GATE_DESCRIPTOR *idt = (struct GATE_DESCRIPTOR *) ADR_IDT; int i; for (i = 0; i <= LIMIT_GDT / 8; i++) { set_segmdesc(gdt + i, 0, 0, 0); } set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, AR_DATA32_RW); set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER); load_gdtr(LIMIT_GDT, ADR_GDT); /* IDT偺弶婜壔 */ for (i = 0; i <= LIMIT_IDT / 8; i++) { set_gatedesc(idt + i, 0, 0, 0); } load_idtr(LIMIT_IDT, ADR_IDT); 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; } bootpack.c 文件 其它处理 /* bootpack偺儊僀儞 */ #include "bootpack.h" #include <stdio.h> void HariMain(void) { struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO; char s[40], mcursor[256]; int mx, my; init_gdtidt(); init_palette(); init_screen8(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(); } }

意犹未尽

naskfunc.nas 中的 _load_gdtr
  • 这个函数用来指定段上限(limit)和地址值赋值给名为GDTR的48位寄存器。给此寄存器赋值的时候,指定一个内存地址,使用指令LGDT从指定的地址读取6个字节 该寄存器的低16位(即内存的最初2个字节)是段上限,它等于“GDT的有效字节数 -1 剩下的高32位(即剩余的4个字节),代表GDT的开始地址。 在最初的时候,DWORD[ESP + 4]里存放的是段上限,DWORD[ESP + 8]里存放的是地址 0x0000ffff 和 0x00270000 [FF FF 00 00 00 00 27 00] (低位放在内存地址小的字节里) 因为是从指定地址读取6个字节,而不是8个,所以希望将上面的变成: [FF FF 00 00 27 00] 使用MOV AX, [ESP + 4],变成 [FF FF FF FF 00 00 27 00] (MOV 指令相当于赋值指令,所以前面的还是FF FF) 如果从[ESP+6]开始读6字节的话,正好是我们想要的结果
set_segmdesc 函数
  • 代码; /* 这个函数是按照CPU的规格要求,将段的信息归结成8个字节写入内存 ❏ 段的大小 ❏ 段的起始地址 ❏ 段的管理属性(禁止写入,禁止执行,系统专用等) struct SEGMENT_DESCRIPTOR { short limit_low, base_low; char base_mid, access_right; char limit_high, base_high; }; 1. 段的地址:地址用32位来表示 — base base 又分为 low(2字节)、mid(1字节)、high(1字节) 2. 段上限:表示一个段有多少个字节,段上限最大是4GB,也就是占用4字节,加上基址(base) 就把整个结构体占满了,所以段上限占用20位。 在段属性里设置了一个标志位:Gbit,这个标志位是1的时候,limit的单位不解释成字节(byte),而解释成页(page),1页是指4KB 4KB * 1M = 4GB,所以可以指定4GB的段 20位段上限分别写到 limit_low 和limit_high中;在limit_high的高四位中写的是段属性 3. 段属性:占用12位 高4位放在limit_high的高4位里(扩展访问权):这4位是由“GD00”构成的 G是指刚才所说的G bit, D是指段的模式,1是指32位模式,0是指16位模式 低8位从80286时代就有了(图看下面) */ 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; } 低八位: CPU有系统模式(也称为“ring0”)和应用模式[插图])和应用模式(也称为“ring3”)之分。操作系统等“管理用”的程序,和应用程序等“被管理”的程序 当应用程序想要使用系统专用的段时,CPU也会中断执行,并马上向操作系统报告

初始化PIC

  • CPU单独只能处理一个中断 PIC是将8个中断信号集合成一个中断信号的装置。PIC监视着输入管脚的8个中断信号,只要有一个中断信号进来,就将唯一的输出管脚信号变成ON,并通知给CPU。 为了处理更多的中断信号,把中断信号设计成了15个,并为此增设了2个PIC。 与CPU直接相连的PIC称为主PIC,与主PIC相连的PIC称为从PIC。主PIC负责处理第0到第7号中断信号,从PIC负责处理第8到第15号中断信号,从PIC通过第2号IRQ与主PIC相连。 int.c的主要组成部分 PIC的初始化程序 #include "bootpack.h" void init_pic(void) /* PIC的初始化 */ { io_out8(PIC0_IMR, 0xff ); /* 禁止所有中断 */ io_out8(PIC1_IMR, 0xff ); /* 禁止所有中断 */ io_out8(PIC0_ICW1, 0x11 ); /* 边沿触发模式(edge trigger mode) */ 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); /* IRQ0-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; } 从CPU的角度来看,PIC是外部设备,CPU使用OUT指令进行操作。程序中的PIC0和PIC1,分别指主PIC和从PIC。PIC内部有很多寄存器,用端口号码对彼此进行区别,以决定是写入哪一个寄存器 端口号码 写在bootpack.h中 /* int.c */ void init_pic(void); #define PIC0_ICW1 0x0020 #define PIC0_OCW2 0x0020 #define PIC0_IMR 0x0021 #define PIC0_ICW2 0x0021 #define PIC0_ICW3 0x0021 #define PIC0_ICW4 0x0021 #define PIC1_ICW1 0x00a0 #define PIC1_OCW2 0x00a0 #define PIC1_IMR 0x00a1 #define PIC1_ICW2 0x00a1 #define PIC1_ICW3 0x00a1 #define PIC1_ICW4 0x00a1
PIC的寄存器
  • 都是8位寄存器 IMR:中断屏蔽寄存器 8位分别对应8路IRQ信号。如果某一位的值是1,则该位所对应的IRQ信号被屏蔽,PIC就忽视该路信号 正在对中断设定进行更改时,如果再接受别的中断会引起混乱,为了防止这种情况的发生,就必须屏蔽中断 ICW:初始化控制数据(ICW有四个) ICW1和ICW4与PIC主板配线方式、中断信号的电气特性等有关 ICW3是有关主—从连接的设定,该从PIC与主PIC的第几号相连,用3位来设定 ICW2,决定了IRQ以哪一号中断通知CPU 中断发生以后,如果CPU可以受理这个中断,CPU就会命令PIC发送2个字节的数据,CPU与PIC用IN或OUT进行数据传送时,有数据信号线连在一起。PIC就是利用这个信号线发送这2个字节数据的,送过来的数据是“0xcd 0x? ? ”这两个字节。由于电路设计的原因,这两个字节的数据在CPU看来,与从内存读进来的程序是完全一样的,所以CPU就把送过来的“0xcd 0x? ? ”作为机器语言执行。这恰恰就是把数据当作程序来执行的情况。这里的0xcd就是调用BIOS时使用的那个INT指令。我们在程序里写的“INT 0x10”,最后就被编译成了“0xcd0x10”。所以,CPU上了PIC的当,按照PIC所希望的中断号执行了INT指令 这次是以INT 0x20~0x2f接收中断信号IRQ0~15而设定的。这里大家可能又会有疑问了。“直接用INT 0x00~0x0f就不行吗,应用程序想要对操作系统干坏事的时候,CPU内部会自动产生INT 0x00~0x1f

中断处理程序的制作

标签:Day6,自制,操作系统
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有