C语言三十天自制操作系统(2)

第4天

今日一最先也学最终学的用汇编写一个能被C语言调用的函数,函数名叫write_mem8,作用是在指定内容中写入一定的数码。这几个函数有七个参数,第三个参数表示目的地址,第四个参数表示写入的多寡。

其一函数最关键的作用就是往指定的内存地址中写入数据,那么用C语言可无法也指定一个内存地址呢?其实是足以的,使用C语言中指针的定义就可以了。C语言中所谓的指针其是就是寻常所谓的内存地址。比如想要往0xa0000内存地址中写入数据,可以这么:

int i;
char* p;
i  = 0xa0000;
p = (char*)i;
*p = i;

那般就把变量i
中的内容写到内存0xa0000中了,而那么些地方就是前面写的操作系统中的显存地址,往这么些地方写入数据就相当于在屏幕上画上内容。

在进入32位以前,用BIOS中断把显卡设置成调色板方式。要写设置显卡中的调色板,只可以利用汇编语言中的in和out指令。首先将屏蔽中断;将想要设定的调色板号码写入0x03c8,按RGB序顺写入0x03c9,即使还要持续写入,只必要后续写入0x03c9;借使要读出调色板状态,将调色板的号子写入0x03c7,再从0x03c9读取3次,调出的顺序是RGB,假使要读取下一个,只要继续从0x03c9中调取下一个调色板;截止将来,裁撤中断屏蔽。

暂停屏蔽指令:

CLI(clear interrupt flag)

撤回中断屏蔽指令:

STI(set interrupt flag)

基于C语言的确定,汇编中放入eax中的数据就是C语言的重回值。

第五天

在32位CPU的格局下操作系统中显示字符和在显示器上画画的原理是相同的,字符只是一种卓殊的图形。本书里把一个字符定义为8*16个像素的位图,那么一个字符就占16字节。一般为了画字符方便,而且操作系统为了协助差别字体作用,会此外单独定义一个字体文件,那个操作系统会在加载内存的时候把字体数据读入内存中。如果定义ASCII编码的256个字符,每个字符16字节,那么一共4096个字节,仍是可以接受。

sprintf(地址, 格式,值,值,值,....)

至于格式的事无巨细表达:

  1. C语言,%d,单纯的十进制数
  2. %5d,5位十进制数
  3. %05d,在前方补0,强制达到时5位
  4. %x,单纯十六进制数字母用小写
  5. %X,单纯十六进制数字母用大写

在显示屏上展现鼠标,也就是在显示屏上画一个图像。本书把鼠标定义为16*16个像素的位图,每个像素是8个字节,总共256个字节。鼠标图像有三种颜色,第一是鼠标的边缘,大家定义为黄色,位图中用*表标;第一个是鼠标本身的颜色,大家定义为黄色,位图中用0表示;第多少个是背景象,就是不突显颜色,用背景观补充,用.代表。

在32位CPU方式下,内存需求分段,为了表示一个段,要求多个新闻:1、段的尺寸;2、段的序幕地址;3、段的治本属性(禁止写入,禁止实施,系统专用等)。CPU用8个字节表示那个信息,但是段寄存器只有16位。表示的法子跟调色板很一般,先有一个段接纳符,存放在段寄存器里,然后在内存中先期设定那8个字节的音讯。16位段寄存器的低3位不可能运用,能用的唯有13位,所以一共能代表8192个段。每个段须要8个字节表示,那么一共须求8192*8=64KB表示,这64KB的多少就是GDT:global
descriptor
table,全局段号记录表。GDT存入在内存的某个地点,然后将内存的前奏地址和有效性设定个数存放在CPU的GDTR(global
descriptor table rigister)中。

IDT就是搁浅记录表(interrupt descriptor
table),当CPU遭逢抛锚后,暂时告一段落正在处理的义务,转而施行中断程序。IDT记录了0~255的刹车号码与调用函数的附和关系,设定方法与GDT很一般。而且要先设定GDT再设定IDT。设定IDT的目标是想让我们的操作系统对鼠标的运动作出反应。

第六天

用C语言写的操作系统源文件bootpack.c已经很长了,现在考虑要将源文件按照区其余功能拆分开来。分成多少个部分:1、关于显示屏上画画的一些graph.c;2、关于GDT\IDT设置的有的dsctbl.c;3、其余处理bootpack.c

鉴于源文件多了相应的Makefile里的编绎规则也就多了。Makefile有一个技艺可以将接近的成形规则

%.gas : %.c Makefile
  $(CC1) -o $*.gas $*.c

%.nas : %.gas Makefile
  $(GAS2NASK $*gas $*.nas

make.exe会先找找普通的变动规则,如果没有找到,就尝试用一般规则。

C语言中的include有<>和“”的区分,前者表示头文件在编绎器的文书夹中,后者表示头文件在源文件所在的文本夹中。

_load_gdtr ; void load_gdtr(int limit, int addr); 
  mov ax, [esp + 4]; limit
  mov [esp + 6], ax
  lgdt [esp + 6]
  ret

本条函数将点名的段上限和地址赋值给GDTR这几个48位寄存器,也就是6个字节。低16位存放段上限,它相当GDT的有效字节数-1,剩余的4个字节代表GDT的初始地址。下面程序把传播的参数limit低2字节移到高2字节,然后实施lgdt指令。

GDT中每一个段音信用8个字节表示,写的操作系统中用一个定义了一个结构体表示。

struct SEGMENT_DESCRIPTOR
{
  short limit_low, base_low;
  char base_mid, access_right;
  char limit_high, base_high; 
};

段的地方也叫做段基础,用32位表示,在结构体中用4个字段表示,分别是base_low(2个字节),
base_mid(1个字节), base_high(1个字节)。正好一共4个字节。

段的轻重也叫做段上限,结构体中用3个字节表示,分别是limit_low(2个字节),limit_high(1个字节)。固然段上限大家社团体里使用了3个字节,可是实际是实惠的只有20位,别的4位表示段属性。limit_high的高4位代表段属性。段大小用20位表示,那么一个段最大的只有1MB。不过足以经过段属性中的一个位设置成1就可以把段上限的单位从字节转换成页,一页表示4KB。那么20位段上限就可以不示4GB。

结构体中还有一个字段access_right,表示段属性,也叫做段的访问权属性。段属性一共用12个字节表示,高4位保存在limit_high字段的高4位中,那4位又被号称增加访问权,这4位由GD00构成,G表示段上限字段的单位是字节依旧页(0表示字节,1代表页)。D字段表示段情势,1是指32位,0是指16位。access_right简单表达如下:

  1. 00000000(0x00): 未使用的记录 段
  2. 10010010(0x92):系统专用,可读写,不可实践
  3. 10011010(0x9a):系统专用,可实施,可读不可写
  4. 11110010(0xf2):应用程序用,可读写,不可实践
  5. 11111010(0xfa):应用程序用,可举行,可读不可写

在动用中断此前一定要先安装PIC,programmable interrupt
controller。电脑的暂停信用有15个,三个PIC,与CPU间接相接的叫做主PIC,与主PIC相连的PIC称为从PIC。从PIC通过2号IRQ与主PIC相连。

PIC的兼具寄存器都是8位的,IMR是interrupt mask
register的缩写,中断屏蔽寄存器。8位分别对应8路IRQ信号,倘使某一位设置为1则忽略该路信号。

ICW是initial control
word的缩写,为开始化控制数据,一共有4个,编号分别为1-4。ICW1、ICW3,ICW4的设定值为一定,操作系统唯一能设置的是ICW2。ICW2表示IRQ以几号中断通知CPU。大家写的操作系统里把015号中断设置为0x200x2f号中断。

汇编指令pushad相当于

push eax
push ecx
push edx
push ebx
push esp
push ebp
push esi
push edi

相对应的也有popad

想让CPU处理搁浅,先写中断的处理程序,然后再把暂停处理程序的进口地址注册IDT中,等到中断信号暴发的时候,系统会自动调用中断处理程序。