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_tools\qemu\fdimage0.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]里存放的是地址
    0x0000ffff0x00270000
    [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

中断处理程序的制作

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值