harib03a:
  内容没有变化 ;P109 从这里开始,代码开始工程化了.
  将原本300多行的bootpack.c分割成了三部分:
        graphic.c      : 用来处理界面图像
        dsctbl.c        : 用来处理中断和段表(GDT,IDT)等
        bootpack.c    : 和后面的bootpack.h文件一起用来封装函数
  修改了Makefile中的文件生成步骤(当然后面需要bootpack.h 头文件):
    修改前:bootpack.c-->bootpack.bim
    修改后:graphic.c  -->graphic.obj
        dsctbl.c    -->dsctbl.obj
        bootpack.c-->bootpack.obj
        graphic.obj+dsctbl.obj+bootpack.obj+其他接口信息-->bootpack.bim
harib03b:
  整理Makefile;简化了Makefile中的一些内容;
  感觉原理和DBMS中的LIKE字符串匹配神似,有木有。
  看看这个书本上的这个例子:归纳成一般规则:

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

harib03c:
  P111 整理头文件;这里实际上还是在做代码优化工作;为后续准备
  我们发现bootpack.c被分割之后,经常需要调用bootpack.c中的函数
  因此,来了一个头文件bootpack.h;把bootpack.c中实现的函数封装起来
  这样每次需要调用函数时只要#include一个bootpack.h 就 OK了!!

/* bootpack.h */
struct BOOTINFO { /* 0x0ff0-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);
/* Cgraphic.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);

  接下来我们来看下LGDT和LIDT是怎么个情况:

  LGDT和LIDT都是分别是给48为的段寄存器GDTR和48位的中断寄存器IDTR赋值,他们的原理基本相同;下面以LGDT为例详细说明一下。

  1、段信息结构体struct SEGMENT_DESCRIPTOR (如下图所示)

    • 包括段的大小;
    • 段的起始地址;
    • 段的管理属性;

    

  按照CPU的结构要求,将段信息归结成8个字节写入内存中;之后再由指令LGDT装入段寄存器GDTR中;

  注意:寄存器GDTR长度为6个字节。写入段寄存器中的信息只是短信息(8个字节)中的高位的6个字节,后面两个字节存储的是段的管理属性-access_right ;

//请对照上面图片看;
//表示段信息的结构体(一共48位)
//段地址 32位:base_low(2字节)+base_mid(1字节)+base_high(1字节)
//剩余4个字节||||我们知道段上限为4GB;一个32位的数值
//如果剩余这4个字节全部用来表示段上限那么就没有空间表示段信息了;
//HOW TO DO??
//段上限 20位:段属性中的标志位Gbit为1时,limit的单位不解释成字节,而翻译成页(1页=4KB)
// limit_low 和limit_high
//注 意:limit_high中的上4位写的是段属性
struct SEGMENT_DESCRIPTOR { //段信息结构体定义8字节
short limit_low, base_low; //2字节
char base_mid, access_right; //1字节
char limit_high, base_high; //1字节
};
struct GATE_DESCRIPTOR { //中断信息结构体定义8字节
short offset_low, selector;
char dw_count, access_right;
short offset_high;
};
/*将段信息的8个字节写入内存;便于段寄存器读取;
;struct SEGMENT_DESCRIPTOR 在头文件中;
;段大小;;段起始地址;;段的管理属性(写入,执行,系统专用等) */
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 >> ) & 0xff;
sd->access_right = ar & 0xff;
sd->limit_high = ((limit >> ) & 0x0f) | ((ar >> ) & 0xf0);
sd->base_high = (base >> ) & 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 >> ) & 0xff;
gd->access_right = ar & 0xff;
gd->offset_high = (offset >> ) & 0xffff;
return;
}

  2、LGDT和LIDT的具体实现

    

;LGDT给48位寄存器GDTR赋值;
;方法:从指定地址的高6个字节;
;底16位为段上限;高32位为GDT表开始的地址
_load_gdtr: ; void load_gdtr(int limit, int addr);
MOV AX,[ESP+] ; limit
MOV [ESP+],AX
LGDT [ESP+]
RET
;LIDT赋值,原理和上面基本相同
_load_idtr: ; void load_idtr(int limit, int addr);
MOV AX,[ESP+] ; limit
MOV [ESP+],AX
LIDT [ESP+]
RET

  3、段管理属性ar(limit_high的高四位+access_right)

    •   在段信息的结构体中,我们可以看到access_right定义类型为char,一个字节(8位);
    •   段管理属性是有两部分组成的:段上限(大小)的高四位(也就是limit_high的高四位)+access_right(8位) = 12位
    •   ar的高四位是“拓展访问权限”
    •   ar的底8位是决定系统模式和应用模式的(有用户进程和系统进程的感觉,有木有)

 harib03d:

  请对照书P117的图看  初始化PIC(programmable interrupt controller-可编程中断控制器),顾名思义:控制中断的东西

  CPU单独只能处理一个中断,因此需要辅助芯片来帮助CPU处理不同的中断信号;

  主PIC(PIC0)和CPU相连;从PIC( PIC1 )和主PIC的IRQ2相连;扩展了16个中断口

  PIC芯片的主要寄存器:

    IMR:中断屏蔽寄存器;8位,对应8路IRQ信号;信号1屏蔽该路中段;

    ICW:初始化控制数据;一共有4个寄存器

    •  ICW1:定值,由主板配线方式决定;
    •  ICW4:定值,由主板配线方式决定;
    •  ICW3:定值,主-从CPI的连接设定;(以上三个都由芯片本身的属性决定)
    •  ICW2:16位,对应从IRQ0-IRQ15;决定PIC芯片发送哪一号中断通知CPU。
/* int.c */
#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, << ); /* 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, ); /* PIC1由IRQ2连接 */
io_out8(PIC1_ICW4, 0x01 ); /* 无缓冲区模式 */ io_out8(PIC0_IMR, 0xfb ); /* 11111011 PIC1以外的全部禁止 */
io_out8(PIC1_IMR, 0xff ); /* 11111111 禁止所有中断 */
return;
}

 harib03e:

  设置键盘和鼠标中断;笔者将键盘中断设在了PIC的IRQ12,将鼠标设在了IRQ1;

   调用的终端号分别为0x2c(0010 1100) 和 0x21(0010 0001)   可以对照P117的PIC芯片图看。

void inthandler21(int *esp)
/* PS/2的键盘中断 */
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO; //加载BIOS
boxfill8(binfo->vram, binfo->scrnx, COL8_000000, , , * - , );
//获取键盘中断之后,显示这个字符串INT 21 (IRQ-1) : PS/2 keyboard
putfonts8_asc(binfo->vram, binfo->scrnx, , , COL8_FFFFFF, "INT 21 (IRQ-1) : PS/2 keyboard");
for (;;) { //之后 让CPU等着 !!
io_hlt();
}
}

  下面介绍了键盘中断函数的具体实现的汇编代码;

  笔者在这里介绍了堆栈的一些知识,这段汇编代码就比较简单了:

_asm_inthandler21:
PUSH ES ;关键寄存器压栈,函数必做
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS ;将DS和ES调整到与SS相等
MOV DS,AX
MOV ES,AX
CALL _inthandler21 ;调用函数_asm_inthandler21
POP EAX ;返回后,将寄存器回复原来
POPAD
POP DS
POP ES
IRETD ;最后执行IRETD

  我们已经编写了键盘的响应函void inthandler21(int *esp);也理解了该函数的汇编具体在内存中怎么做的_asm_inthandler21:

  接下来就要把键盘中断号写到中断表中,这样当执行INT时,就可以调用相应的中断程序了(在这里是函数void inthandler21(int *esp))

    /* IDT设定; */
set_gatedesc(idt + 0x21, (int) asm_inthandler21, * , AR_INTGATE32);
set_gatedesc(idt + 0x27, (int) asm_inthandler27, * , AR_INTGATE32);
set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, * , AR_INTGATE32);

  差不多了,make run 以后,只要敲了键盘,就会相应0x21中断,接着CPU会在中断表中找到这个中断程序(在这里是函数void inthandler21(int *esp))的入口了。于是就会执行这个中断程序,而这个程序的内容就是输出字符串

    INT 21 (IRQ-1) : PS/2 keyboard

《30天自制操作系统》06_day_学习笔记的更多相关文章

  1. 《30天自制操作系统》学习笔记--Mac下工具的使用

    现在来介绍官网上下的工具怎么用首先是官网地址,书上有个注释上有:hrb.osask.jp 翻译成中文大概是这个样子滴. 上面有两个文件可以下载,一个是工具,一个是工具的源代码,很好的学习资料 下面把工 ...

  2. 《30天自制操作系统》学习笔记--Mac环境搭建

    弄了三天了,终于弄好了,先说结果,就是作者在网站上放了os x的工具(hrb.osask.jp,也有linux下的工具,可以自己去下载),也就是说我白忙活了三天... 再说一下这几天都干啥了,主要是想 ...

  3. 《30天自制操作系统》学习笔记--番外篇之Mac环境下的工具介绍

    这几天又有点不务正业了,书也没看,一直在搞这个破环境,尝试各种做法,网上各种垃圾信息,浪费了很多时间,说的基本都是废话,不过还是找到了一些,赶紧写下来,不然这个过几天又忘了 首先是环境,我用的是Max ...

  4. 《30天自制操作系统》读书笔记(5) GDT&IDT

    梳理项目结构 项目做到现在, 前头的好多东西都忘了, 还是通过Makefile重新理解一下整个项目是如何编译的: 现在我们拥有这么9个文件: ipl10.nas    InitialProgramLo ...

  5. 《30天自制操作系统》读书笔记(3) 引入C语言

    这一次的学习相当曲折, 主要是因为粗心, Makefile里面的错误导致了文件生成出现各种奇奇怪怪的问题, 弄得心力交瘁, 因此制作过程还是尽量按着作者的路子来吧. 作者提供的源码的注释在中文系统下是 ...

  6. 《30天自制操作系统》读书笔记(2)hello, world

    让系统跑起来 要写一个操作系统,我们首先要有一个储存系统的介质,原版书似乎是06年出版的,可惜那时候没有电脑,没想到作者用的还是软盘,现在的电脑谁有软驱?不得已我使用一张128M的SD卡来代替,而事实 ...

  7. 30天自制操作系统第九天学习笔记(u盘软盘双启动版本)

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第九天的课程已学完,确实有点不想写 ...

  8. 从你的u盘启动:30天自制操作系统第四天u盘启动学习笔记

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 developing environment:ubuntu 关于u盘启动自己做的操 ...

  9. 30天自制操作系统第八天学习笔记(u盘软盘双启动版本)

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第八天的学习思考: 关于鼠标是怎么 ...

  10. 《30天自制操作系统》笔记(03)——使用Vmware

    <30天自制操作系统>笔记(03)——使用Vmware 进度回顾 在上一篇,实现了用IPL加载OS程序到内存,然后JMP到OS程序这一功能:并且总结出下一步的OS开发结构.但是遇到了真机测 ...

随机推荐

  1. 读取C#AssemblyInfo文件中的AssemblyVersion中的值

    System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); 1.程序集的版本信息由下面四个值组成:主 ...

  2. jquery()的三种$()

    jQuery中的$以及选择器总结 $号是jQuery”类”的一个别称,$()构造了一个jQuery对象.所以,”$()”可以看作jQuery的”构造函数”(个人观点). 一.$符号 1.$()可以是$ ...

  3. he time that it takes to bring a block from disk into main memory

    DATABASE SYSTEM CONCEPTS, SIXTH EDITION There is a trade-off that the system designer must make betw ...

  4. overloading

    Computer Science An Overview _J. Glenn Brookshear _11th Edition_C Many programming languages allow t ...

  5. w_all_checked - js -checkbox 多选、全选、submit、request

    <!doctype html> <html> <head> <meta charset="UTF-8"> </head> ...

  6. JVM内存配置

    JVM内存主要分为两个部分,分别是PermanentSapce和HeapSpace. PermantSpace主要负责存放加载的Class类级对象如class本身,method,field等反射对象, ...

  7. UML 关系

    1. 关联关系(association) 关联关系式是用一条直线表示的,如A—B.表示在一段时间内将多个类的实例连接在一起,关联关系描述了某个对象在一段时间内一直知道另一个对象的存在.在Rose中为了 ...

  8. svn解锁

    SVN解锁操作 现在很多项目开发都使用SVN作为馆控工具,SVN馆中的文件既可以以文件夹的方式获取,也可以通过eclipse导入.获取文件后,我们可以对某个文件锁定. 如果某个同事锁定了某个文件,而他 ...

  9. pro10

    1.本次课学习到的知识点: 什么是数组?为什么要使用数组?如何定义数组? 如何引用数组元素? 2. 实验过程中遇到的问题及解决方法: 在最后一个试验中,在输出最大小标那里进行了仔细的思考,刚开始思考应 ...

  10. nRF51822之pstorage使用摘要

    https://devzone.nordicsemi.com/question/15271/how-can-i-write-10kb-of-data-to-internal-flash/?answer ...