第六天 - 分割编译与中断处理

第六天 - 分割编译与中断处理

一、文件整理

1. 源文件分割

  • 按照内容对源文件进行分割为多个小文件,修改和编译都会提高make的速度

  • image-20230319115156497

graphic.c - 描画等图形处理

/* u关于描画的处理 */

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 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

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, 0x00, 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;

    /* C语言中的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;	/* date */
	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)
/* 准备鼠标指标 (16 x 16) */
{
    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 — GAT,IDT相关

/* 关于 GDT、IDT 等 descriptor table 的处理 */

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);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);

void init_gdtidt(void)
{
	struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;
	struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) 0x0026f800;
	int i;

	/* GDT的初始化 */
	for (i = 0; i < 8192; i++) {
		set_segmdesc(gdt + i, 0, 0, 0);
	}
	set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);
	set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a);
	load_gdtr(0xffff, 0x00270000);
	
	/* IDT的初始化 */
	for (i = 0; i < 256; i++) {
		set_gatedesc(idt + i, 0, 0, 0);
	}
	load_idtr(0x7ff, 0x0026f800);

	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 <stdio.h>

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 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

struct BOOTINFO {
    char cyls, leds, vmode, reserve;
    short scrnx, scrny;
    char *vram;
};

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);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);

void HariMain(void)
{
    struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
    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();
    }
}

对应的Makefile流程

image-20230319115423703

2. 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
      
  • make.exe 会优先寻找普通的生成规则,找不到再尝试一般规则

3. 头文件

  • 将各个源文件相同的函数声明等内容,整合到一个头文件中,减少代码冗余

  • 使用头文件只需要

    • #include "头文件名.h"
      
  • 使用头文件的规则

    使用 双引号引用头文件,表示头文件和源文件在同一个文件夹,而尖括号 '< >' 引用头文件表示该头文件位于编译器所提供的文件中

  • 当编译器看到该行引用,就会把该行替换为所指定文件的内容,然后编译

bootpack.h

/* asmhead.nas */
struct BOOTINFO {	/* 0xff0-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 {
	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

4.放到第五天

5. 初始化PIC - 可编程中断控制器

  • programmable interrupt controller

  • 为了解决CPU单独只能处理一个中断的问题,提出了PIC,PIC是8个中断信号集成一个中断信号的装置,只要有一个中断信号进来,就会将输出引脚改为ON并通知CPU处理中断

  • 可以两个PIC相连,可以处理15个中断信号,称之主PIC和从PIC

    • image-20230319180852566

init.c - PIC 初始化程序

/* 与中断有关 */
#include "bootpack.h"

void init_pic(void)
/* PIC的初始化 */
{
	io_out8(PIC0_IMR,  0xff  );	/* 禁止所有中断 */
	io_out8(PIC0_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;

}
  • PIC 对于CPU来说是外设,需要通过OUT命令进行操作,PIC0 位主PIC, PCI1为从PIC

PIC中的寄存器用端口区分(bootpack.h),都为8位寄存器

IMR
  • interrupt mask register, 中断屏蔽寄存器
  • 8位分别对应8路IRQ信号,若其中一位为1,则PIC屏蔽其信号
  • 作用
    • 正在对终端设定进行修改时,为防止混乱,暂停接受中断信号
ICW
  • initial control word,初始化控制数据
  • ICW1,4(主板的配线方式,中断信号的电气特性等相关)
  • ICW3与 主从连接的设定有关
  • ICW2可以独立设定,用于设定中断号,决定IRQ以哪一号中断通知CPU

6. 中断处理程序的制作

中断处理程序

  • 鼠标是IRQ12,键盘是IRQ1,他们的中断处理程序如下:
void inthandler21(int *esp)
/* 来自PS/2键盘的中断 */
{
	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();
	}
}
  • 中断处理程序知识显示一条信息,然后保持待机状态

执行IRETD指令

  • 中断处理程序完成后,不能return返回,而是必须执行IRETD指令(借助汇编语言修改)

  • 该函数执行流程

    • 将寄存器的值保存到栈中
    • 然后将DS和ES调整到与SS相等
    • 最后调用_inthandler21函数
    • 函数返回后,将所有的寄存器再返回原来的值
    • 最后执行IRETD
_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

先进先出的缓冲区(队列),简称FIFO

  • image-20230319183023765

后进先出的缓冲区(栈),简称FILO

  • image-20230319183013182

  • PUSH EAX 这个压栈操作相当于

    • ADD ESP, -4
      MOV [SS:ESP], EAX
      
  • POP EAX

    • MOV [SS:ESP], EAX
      ADD ESP, 4
      
  • PUSHAD 指令

    • image-20230319183522343
  • POPAD 相对于PUSHAD进行弹栈,恢复寄存器的原来的值,然后再执行IRETD进行返回

将中断处理函数注册到IDT中

  • /* IDT的设定 */
    set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);
    set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32);
    
  • asm_inthandler21注册在idt的 0x21号,2*8表示asm_inthandler21属于段2,即

    • set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER);
      
    • AR_CODE32_ER将IDT的属性设为0x008e,即表示用于中断处理的有效设定

  • 设定完成后,当发生了相应的0x21号中断后,就会执行相应的中断处理函数

bootpack.c - HariMain 补充分析

/* 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_pic();
    io_sti();

    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);

    io_out8(PIC0_IMR, 0xf9);
    io_out8(PIC1_IMR, 0xef);

    for (;;) {
        io_hlt();
    }
}
  • io_sti() 执行STI执行,即CLI的逆指令,执行了STI后,IF(interrupt flag)中断标志位为1,CPU就可以接受外部设备的中断

  • HariMain最后修改了PIC的IMR,以便接收来自键盘和鼠标的中断

总结

  • 命令行 make run

image-20230402212329381

posted @   bingekong  阅读(28)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示