操作系统简单轮转调度算法的原理(第06天分割文件中断处理30天自制操作系统学习笔记)
操作系统简单轮转调度算法的原理(第06天分割文件中断处理30天自制操作系统学习笔记)bootpack.gas : bootpack.c Makefile $(CC1) -o bootpack.gas bootpack.c graphic.gas : graphic.c Makefile $(CC1) -o graphic.gas graphic.c dsctbl.gas : dsctbl.c Makefile $(CC1) -o dsctbl.gas dsctbl.c文件名不同,执行内容雷同!简化一下#include "bootpack.h"整理MakefileMakefile内容也很长,简化一下为了解决重复填写顶部声明问题,引入了公共文件。新建:bootpack.h/* asmhead.nas */ struct BOOTINFO { char cyls; char leds; char vmode; char reser
分割bootpack.c分割优势:内容分类管理,易于后期维护
分割劣势:文件数量增多,容易搞混
分割成三个文件
预警!想使用naskfunc.nas的功能,每个文件都要填写顶部声明。
这样太麻烦了,后期增加内容,需要写3次!
为了解决重复填写顶部声明问题,引入了公共文件。
新建:bootpack.h
/* asmhead.nas */
struct BOOTINFO {
char cyls;
char leds;
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 {
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
需要顶部声明的文件,引入公用文件即可
#include "bootpack.h"
Makefile内容也很长,简化一下
bootpack.gas : bootpack.c Makefile
$(CC1) -o bootpack.gas bootpack.c
graphic.gas : graphic.c Makefile
$(CC1) -o graphic.gas graphic.c
dsctbl.gas : dsctbl.c Makefile
$(CC1) -o dsctbl.gas dsctbl.c
文件名不同,执行内容雷同!简化一下
%.gas : %.c Makefile
$(CC1) -o $*.gas $*.c
去掉了具体的文件名,用文件后缀来判断具体要做的事情
bootpack.nas : bootpack.gas Makefile
$(GAS2NASK) bootpack.gas bootpack.nas
graphic.nas : graphic.gas Makefile
$(GAS2NASK) graphic.gas graphic.nas
dsctbl.nas : dsctbl.gas Makefile
$(GAS2NASK) dsctbl.gas dsctbl.nas
文件名不同,执行内容雷同!简化一下
%.nas : %.gas Makefile
$(GAS2NASK) $*.gas $*.na
其他文件也可以这样操作~
接着讲讲naskfunc.nas内容
_load_gdtr: ; void load_gdtr(int limit int addr); ; 段上限limit和地址数据
MOV AX [ESP 4]
MOV [ESP 6] AX
LGDT [ESP 6]
RET
GDTR是一个很特别的48位寄存器
不能用MOV 指令来赋值。
唯一的方法就是先指定一个内存地址,然后从指定的地址读取6个字节(也就是48位),最后赋值给GDTR寄存器。
完成这一任务的指令就是LGDT。
再讲讲dsctbl.c内容(这是bootpack.c分割出来的文件)
struct SEGMENT_DESCRIPTOR {
short limit_low base_low;
char base_mid access_right;
char limit_high base_high;
};
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的规格要求,将段的信息归结成8个字节写入内存的。
这8个字节里到底填入了什么内容呢
- 段的上限(最大4GB)
- 段的基址(32位bit=low16位bit mid8位bit high8位bit)
- 段的访问权属性(禁止写入,禁止执行,系统专用等)
CPU到底是处于系统模式还是应用模式,取决于执行中的应用程序是位 于访问权为0x9a的段,还是位于访问权为0xfa的段
- 想要移动鼠标?必须使用中断!
- 想要使用中断?将GDT和IDT正确无误地初始化!
- 想要使用中断?将PIC正确无误地初始化!
所谓PIC是“programmable interrupt controller”的缩写,意思是“可编程中断控制器”。
在设计上,CPU单独只能处理一个中断,这不够用!
所以增设了几个辅助芯片,中断信号共有15个,PIC共有2个。
PIC是将8个中断信号集合成一个中断信号的装置。
只要有一个中断信号进来,就将唯一的输出管脚信号变成ON,并通知给CPU。
从CPU的角度来看,PIC是外部设备,CPU使用OUT指令进行操作。
PIC内部有很多寄存器,用端口号码对彼此进行区别,端口写在bootpack.h文件中
更新: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
引用端口
新建:int.c文件(老规矩,先建txt,改后缀)
#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 ); /* 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;
}
最后在bootpack.c文件中调用
#include "bootpack.h"
#include <stdio.h>
void HariMain(void)
{
init_gdtidt();
init_pic();
......代码脑补
}
前面讲了很多理论,该制作中断处理功能
鼠标是IRQ12,键盘是 IRQ1
所以我们编写了用于INT 0x2c和INT 0x21的中断处理程序
更新:int.c
/*------------键盘-----------------*/
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 "hello world my keyboard");
for (;;) {
io_hlt();
}
}
/*---------------鼠标------------*/
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 "hello world my mouse");
for (;;) {
io_hlt();
}
}
更新:bootpack.c
#include "bootpack.h"
#include <stdio.h>
void HariMain(void){
io_out8(PIC0_IMR 0xf9);
io_out8(PIC1_IMR 0xef);
....脑补代码
}
此时还有问题,C语言无法解决,所以搬来汇编救场
更新:naskfun.nas
EXTERN _inthandler21 _inthandler2c
_asm_inthandler21:
PUSH ES
PUSH DS
PUSHAD
MOV EAX ESP
PUSH EAX
MOV AX SS
MOV DS AX
MOV ES AX
CALL _inthandler21
POP EAX
POPAD
POP DS
POP ES
IRETD
push和pop叫做栈(stack)
起到临时救场,俗称缓冲区(buffer)
比如一下子涌入大量信息,先到缓冲区排队,然后再读取。
先进入缓冲区的不一定先读取,还可以最后读取,所以使用时要搞清楚缓冲区类型。
说了这么多,是驴子是马拉出来走走
测试:打开cmd,输入make run
此时按下键盘上的任意键,出现了设置的文字 键盘输入成功!
嗨~ 各位老铁们
鼠标移到窗口内,点击一下左键,进入到了咱们的操作系统世界
发现鼠标没反应,这是因为还需要设置其他内容,今天肯定是别想移动了。
按住ctrl alt可以切换到windows系统
今天就到这里,洗洗睡了