三十天自制操作系统(3)

第7天

PIC初始化之后,再写中断处理函数,然后把中断处理函数的入口地址注册在IDT中。现在重点是中断处理函数如何编写。

PIC中还有一个寄存器OCW,如果键盘发生中断,需要向PIC发送0X60+IRQ号码,执行这行代码之后PIC才会继续监视IRQ1的中断是否发生。OCW设置完毕后,CPU再从端口中读入键盘数据,计算机的硬件规定,键盘数据总共8位,从0x0060端口读入。附上中断处理程序

void inthandler21(int *esp)
{
  struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
  unsigned char data, s[4];
  io_out8(PIC0_OCW2, 0x61); 
  data = io_in8(PORT_KEYDAT);
  sprintf(s, "%02X", data);
  boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 23, 31);
  putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
  return;
 }

像上面的程序那样在中断处理程序中既要读取端口数据又要显示到屏幕上,作大量的图像处理工作,如果这个时候正好有一个中断进来,计算机就不能处理,只能不作任何反应。为了不影响中断处理,把inthandle21中断处理程序中的图像处理部份拿出来,inthandle21中断处理程序只做一个工作:读取端口数据,把数据放入缓存区,把图像处理工作放在主程序中处理。主程序一直查询缓存区是否有数据,如果有数据就把数据显示到屏幕上。接下来重点就是怎么写缓存区的程序了。

键盘的缓冲区程序其实就是大学学过的队列,也就是能保证先进先出的数据结构。当然我们在写操作系统当然无法用链表,只能用数组实现一个队列。

先定义一个队列的结构体

struct FIFO8 
{
  unsigned char *buf;
  int p;//下一个数据定入地址
  int q;//下一个数据读出地址
  int size;//队列长度
  int free;//表示队列有没有数据的字节数
  int flags;//标记队列有没有溢出,最低位是1表示溢出
};

第一步:初始化

void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
{
  fifo->size = size;
  fifo->buf = buf;
  fifo->free = size; 
  fifo->flags = 0;
  fifo->p = 0; 
  fifo->q = 0; 
  return;
}

第二步:存入数据

int fifo8_put(struct FIFO8 *fifo, unsigned char data)
{
  if (fifo->free == 0) 
  {
    fifo->flags |= FLAGS_OVERRUN;
    return -1;
  }
  fifo->buf[fifo->p] = data;
  fifo->p++;
  if (fifo->p == fifo->size) 
  {
    fifo->p = 0;
  }
  fifo->free--;
  return 0;
}

第三步:读取数据

int fifo8_get(struct FIFO8 *fifo)
{
  int data;
  if (fifo->free == fifo->size) 
  {
    return -1;
  }
  data = fifo->buf[fifo->q];
  fifo->q++;
  if (fifo->q == fifo->size) 
  {
    fifo->q = 0;
   }
  fifo->free++;
  return data;
}

附:查询数据量

int fifo8_status(struct FIFO8 *fifo)
{
  return fifo->size - fifo->free;
}

键盘的中断码是IRQ1,而鼠标则要晚得多,是IRQ12。如果要让鼠标操作有效必须发行指令,让下面两个装置有效,一个是鼠标控制电路,一个是鼠标本身。首先要激活鼠标控制电路,鼠标控制电路包含在键盘控制电路里。然后发送激活鼠标的指令,其实发送这个指令实际上就是CPU发送数据到鼠标控制器,也就是键盘控制器。

鼠标的中断处理程序和鼠标差不多,也就是从端口中读取数据放入队列中,甚到CPU读取数据的端口号都是一样的。然后在主程序中从鼠标队列中取出数据进行处理。键盘的处理非常简单,从端口读进来的数据就是键盘的扫描码。那么从端口读进来的鼠标数据表示什么意思呢?

第八天

鼠标一开始设置完成会自动发生一次中断,这次中断发送到CPU的数据为0xfa,只是表示鼠标已经设置完成,将会向CPU发送数据。我们每次对鼠标操作都会引起鼠标向CPU发送三次中断,每次中断发送一个字节,一共三个字节,我们要把这三个字节凑到一起处理才是有意义的。

鼠标一次性接收3字节数据,其中第一个字节表示鼠标的动作,第一个字节的高4位取值范围是03,如果出现其它值,说明鼠标出现错误。第一个字节的低4位取值范围8F,如果出现其他值也说明鼠标发生错误,如果第0位为1说明鼠标左键按下,如果第1位为1说明右键被按下。第二个字节为鼠标水平方向移动的多少,正值为右,负值为左。第三个字节为鼠标竖直方向移动多少,正值向上,负值向下。

目前已经能处理鼠标操作传入的3次中断数据,现在就是简单得在画面上显示出来。首先把原来鼠标图像所在的位置给画成背景色,然在计算新的鼠标位置,然后在新的鼠标位置上画一下鼠标图像,然后就感觉鼠标移动了。

接下来插入讲解一下如何从CPU实模式跳入保护模式。我们首先把显卡模式设置好,然后把一些需要BIOS做的工作给做好。将下来就开始跳入保护模式了。关掉CPU级别中断,往ox60号端口写入0xdf,可以让CPU使用超过1M的内存容量。然后设置CR0寄存器,把CR0寄存器读出来,然后把最高位和最低位设置为0,再放入CR0,CPU就跳入保护模式了,进入保护模式后马上要执行JMP指令,才能使接下来的指令正常执行。接下来只要把特定的程序复制到内存中就行了。也就是IDT,GDT,主程序,栈及其他这个所放的位置在内存中放好就行了。

保护模式和实模式的的区别就在于计算内存地址时,是使用段寄存器的值直接指定地址值的一部份呢,还是通过GDT 使用段寄存器的值指定并非实际存在的地址号码。

第九天

鼠标处理告一段落,这一天主要做内存管理,内存管理的第一步是检测计算机内存容量有多大。

先检测CPU有没有缓存,如果CPU是486以上就有缓存,以下就没有缓存也就不需要关闭缓存。检测方法就是看往CPU标志寄存器第18位写入是否有效。然后对CR0寄存器的某些位设置为0,才能关闭掉缓存。关掉缓存之后,开始检测内存容量,检测的方法也很简单,先把内存地址从小到大,每次读一个内存内容,然后把内存内容取否,写入内存,再读一次这个内存,如果新的内容跟预计取反的内容一样说明这个内存地址是有效的。为了提高效率我们没有一个字节一个字节检测,而是每次检测4KB,所以这4KB内存中最一4个字节是有效的,那就认为这4KB都是有效内存。内存大小知道了之后,就可以进行内存管理了。

内存管理的基础是内存分配和内存释放。内存是否使用可以在内存中做一个位图来表示,比如内存有16MB,我们以4KB为一个分配单位,那么一共需要4096位标记,也就是512字节。在这512字节中,如第1位是0,就表示0~0xfff这个内存地址未使用。如果以这种方式管理内存,从代码上来讲比较简单。但是也有不足,那就是分配最小内存地址不灵活,上面的例子中以4KB为一个单位分配,如果只需要1B,那么这样分配就太浪费了。如果位图以1字节为单位的话,那么位图就需要16777216个标记位,也就是2MB个标记位,太浪费了。

还有一种就是这本书使用的内存管理方式,类似于从XXX号地址开始的YYY字节的空间是空着的。可以创建这样一种数据结构

struct FREEINFO
{
  unsigned int addr, size;
}

struct MEMMAN
{
  int frees;
  struct FREEINFO free[1000];
}
struct MEMMAN memman;
memman.frees = 1;
memman.free[0].addr = 0x400000;
memman.free[0].size = 0x07c00000;

如果需要100KB的空间,查看memman中free的情况,找出100MB以上的可用空间即可

for(int i = 0; i < memman.frees; i++)
{
  if(memman.free[i].size >= 100 * 1024)
  {
    "找到可用空间';
    "从地址memman.free[i].addr开始的100KB空间; 
  }
}

用这种方法可以实现内存的管理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容

  • 8086汇编 本笔记是笔者观看小甲鱼老师(鱼C论坛)《零基础入门学习汇编语言》系列视频的笔记,在此感谢他和像他一样...
    Gibbs基阅读 37,074评论 8 114
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,555评论 18 139
  • 桃源口, 沙溪洲, 霜叶寒江不堪秋。 梦秦楼, 秦楼明月照人愁。 燕去盼燕归, 人生几回头。 望迢迢, 销魂谁人晓...
    思飨反动阅读 448评论 0 4
  • 金缮是漆艺中非常小的一块,简而言之就是把漆当胶水用。但是前段时间金缮好像真的是火到不行,国内网上关于金缮的“实在”...
    73e554e80a99阅读 16,987评论 2 3
  • 6月7日 星期三 阴转晴 今天早晨叫儿子起床,叫了好几遍,儿子才不情愿的起床,洗脸,刷牙,吃完饭一看...
    一年级八班崔皓媛阅读 120评论 0 0