20天之前的一些笔记

来源:互联网 发布:flicker free for mac 编辑:程序博客网 时间:2024/05/16 18:54
0x00007c00-0x00007dff           :启动区内容的装载地址


*******************************************************************

磁盘的操作:

INT 0x13:磁盘读写,扇区校验以及寻道
AH=0x02读盘
AH=0x03写盘
Ah=0x04校验
AH=0x05寻道
AL=处理对象的扇区数(只能同时处理连续的扇区)
CH=柱面号 & 0xff
CL=扇区号(0-5位)|(柱面号 & 0x300)>>2
DH=磁头号
DL=驱动器号
ES:BX=缓冲地址;(校验及寻道时不使用)
返回值:
FLAGS.CF==0 : 没有错误,AH=0
FLAGS.CF==1 : 有错误,错误号码存入AH内


eg:
MOV AX,0x0820
MOV ES,AX
MOV CH,0柱面0
MOV DH,0磁头0
MOV CL,2扇区2


MOV AH,0X02读盘
MOV AL,11个扇区
MOV BX,0
MOV DL,0X00A驱动器
MOV DL,0X00调用磁盘BIOS
INT 0x13
JC  ERROR错误则跳转到错误信息显示页面
********************************************************************
; 在系统进入32位模式之前, 将一些基本的信息存储以便方便使用

; 有关BOOT_INFO


CYLSEQU0x0ff0; 设定启动区
LEDSEQU0x0ff1
VMODEEQU0x0ff2; 有关颜色数目的信息,颜色的位数
SCRNXEQU0x0ff4; 分辨率的X
SCRNYEQU0x0ff6; 分辨率的Y
VRAMEQU0x0ff8; 图像缓冲区的开始地址
; 对于INT 0x10,这种画面模式下VRAM是0xa0000-0xaffff


ORG0xc200; 程序将要被放到的地方
MOVAL,0x13; VGA显卡,320x200x8bit位彩色
MOVAH,0x00
INT0x10
MOVBYTE [VMODE],8; 记录画面模式
MOVWORD [SCRNX],320
MOVWORD [SCRNY],200
MOVDWORD [VRAM],0x000a0000


; 用bios取得键盘上各种LED灯的状态


MOVAH,0x02
INT0x16 ; keyboard BIOS
MOV[LEDS],AL




当要显示一些信息的时候,需要从相应位置读取一些信息
struct BOOTINFO {
char cyls, leds, vmode, reserve;
short scrnx, scrny;
char *vram;
};
struct BOOTINFO *binfo;定义结构体变量
init_palette();将相应的颜色信息写到相应的位置
binfo = (struct BOOTINFO *) 0x0ff0;于相应位置取出相应的颜色信息
xsize = binfo->scrnx;
ysize = binfo->scrny;
vram  = binfo->vram;取出颜色信息


********************************************************************

//对于第四天里面的文件:

ipl10.nas启动扇区,用来加载其余的十个扇区
asmhead.nas在16位模式时写入内存一些东西,并跳转到32位模式
bookpack.c进入32位模式之后将要被执行的程序(c语言格式)
naskfunc.nasbookpack.c中使用的函数(C)的声明和定义。
hankaku.txtASCII编码的点阵


//对于第六天的文件:
由于bookpack.c太大而将其分为
graphic.c关于描画的处理
dsctbl.c关于GDT,IDT等descriptor table的处理
bookpack.c其他处理
同时,因为很多重复定义,因此将原来的bookpack文件里面定义的变量等放到
了bookpack.h文件里面。


*******************************************************************

CPU与外部设备进行通信


OUT向设备发送电信号
IN从设备读取电信号
(设备号叫做端口port)
而且通常在进行设备操作的时候,为了防止随时可能发生的中断的影响,需要
先屏蔽终端,操作结束时恢复中断,其过程是:
int eflag;
eflag=io_load_eflags();记录中断许可标志的值
io_cli();将中断许可标志置为0,屏蔽中断
CPU work with the device;
io_store_eflags(eflag);恢复中断许可标志


********************************************************************
GDT与IDT

段的信息:
1:段的大小是多少
2:段的起始地址在哪里
3:段的管理属性(禁止写入,禁止执行,系统专用等)


GDT:全局段号记录表(OSAKA为0x270000-0x27ffff)
由于段寄存器是16位,而其低三位是不能使用的,因此我们能够使用的段号就
只有13位,即0-8191。因此需要8192*8=65536个字节(64K)的存储空间
而这64K的数据就称为GDT。
将这64K的数据整齐的排列在内存的某个地方,然后将内存的起始地址和有效
设定个数放在CPU中被称作GDTR的寄存器中,设定就完成了。


IDT:中断记录表(OSAKA为0x26f800-0x26ffff)


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


//IDT
struct GATE_DESCRIPTOR {
short offset_low, selector;
char dw_count, access_right;
short offset_high;
};




对于GDTR寄存器,它是48位寄存器,因此不能用一般的mov指令进行赋值。
这时就需要指定一个内存地址,并通过LGDT来进行赋值。
GDTR的低16位表示段的上限,高32位表示段的起始地址。


对于IDTR寄存器,用法与GDTR一样


对于SEGMENT_DESCRIPTOR 结构体,
base_low2字节
base_mid1字节
base_high1字节
段上限只有20位,剩余的4位要存储段属性。而这时段上限表示的最大范围是
1M,因此不合适。所以在段属性里面有一位Gbit位,若Gbit位1,limit的单
位不解释成byte,而是page,即4KB,这样20位的段上限就可以表示4G了
limit_low2字节
limit_high1字节(只有低四位表示段上限,高四位则表示段属性)


access_right1字节(段属性的低八位在ar里,高八位在limit_high中)
为了处理方便,常按照形式处理:xxxx0000xxxxxxxx
高4位被称为“扩展访问权”   GD00   G为Gbit,D为段的模式,
                              1指32位模式,0指16位模式


低八位:
00000000:未使用的记录表
10010010:系统专用,可读写,不可执行
10011010:系统专用,可执行,可读不可写
11110010:应用程序用,可读写,不可执行
11111010:应用程序用,可执行,可读不可写


********************************************************************
内存分布图

第八天,P158
0x00000000 - 0x000fffff :在OS启动中多次用到,但在之后就变空(1M)
0x00100000 - 0x00267fff  :用于保存软盘的内容(1440K)
0x00268000 - 0x0026f7ff :空  (30K)
0x0026f800 - 0x0026ffff :IDT   (2K)
0x00270000 - 0x0027ffff :GDT  (64K)
0x00280000 - 0x002fffff :bootpack.hrb (512K)
0x00300000 - 0x003fffff :栈及其他   (1M)
0x00400000 - :空

对于本书

1号GDT记录的是一个从0x00000000开始到0xFFFFFFFF的段。

2好GDT记录的是bookpack.hrb所在的段 

********************************************************************
内存检查:

为了进行内存检查,我们应关闭高速缓存:
关闭高速缓存的前提需要检查cpu是386还是486及其以上型号
若为386,则EFLAG的18位恒为0,无法改变
若为486,则EFLAG的18位可以改变。


关闭高速缓存工作需要对cr0进行操作


*******************************************************************

内存简单管理方法:
1:分块管理:
将内存按照固定大小(如1KB)分成n块,并将这n快内存的状态存储
在固定位置。
2:列表管理:
把类似“从XXX号地址开始的YYY个字节都是空着的”这种信息存在
表里。


********************************************************************


struct FREEINFO {// 可用信息
unsigned int addr, size;
};


struct MEMMAN {// 内存管理
int frees, maxfrees, lostsize, losts;
struct FREEINFO free[MEMMAN_FREES];
};


********************************************************************
图层管理(窗口的显示,隐藏,叠加等)

/* sheet.c */
#define MAX_SHEETS 256
struct SHEET {
unsigned char *buf;//记录图层上所描绘内容的地址
int bxsize, bysize;//图层的大小
int vx0, vy0;//图层的位置坐标
int col_inv;//颜色和透明属性
int height;//图层高度
int flags;//图层的设定信息

};
struct SHTCTL {
unsigned char *vram;//图像缓存的地址
int xsize, ysize, top;//xsize,ysize表示屏幕的信息,top为最上面图层的高度
struct SHEET *sheets[MAX_SHEETS];
struct SHEET sheets0[MAX_SHEETS];
};

其中 sheet0[n] 中表示第n+1个图层的具体信息
然后将里面的图层的高度按照升序排列,将其地址存入 sheet[n] 指针中。


********************************************************************
本书中绘制一个窗口的步骤:

若想要绘制一个窗口,则应经过以下步骤:
1:申请一块内存,大小为希望绘制窗口的大小
2:在这块内存上面绘制图案
3:将图层管理器SHTCTL中的sheets0[n]->buf指向这块内存地址,并指定其
高度


********************************************************************

PIT:可编程的间隔型计时器(PIT连接着IRQ的0号)


AL=0x34OUT(0x43,AL);
AL=中短周期的低八位OUT(0x40,AL);
AL=中短周期的高八位OUT(0x40,AL);


若指定中断周期为0,则相当于被指定65536
本书中指定中断周期为11932,则中断的频率为100Hz


实际操作:
#define PIT_CTRL 0x0043
#define PIT_CNT0 0x0040
io_out(PIT_CTRL, 0x34);
io_out(PIT_CNT0, 0x9c);
io_out(PIT_CNT0, 0x2e);


若想要使用这个值,应在中断程序中增加一个全局变量counter,每调用一次
中断,counter自动加一。因此,这个counter可以用来表示OS开始运行的时间


********************************************************************

显卡显示模式


320*200是老的(不使用VBE)显示模式
而新的(使用VBE)显示模式叫做VBE


切换到老的显示模式:AH=0,AL=画面模式号码
切换到新的显示模式:AX=0x4f02,BX=画面模式号码


VBE的画面模式号码:
0x101...640*480*8bit
0x103...800*600*8bit
0x105...1024*768*8bit
0x107...1280*1024*8bit
另外,qemu无法使用0x107画面模式,且实际指定的时候,需要将画面模式号码
增加0x4000


确认VBE是否存在:
为 ES 赋值0x9000
为 DI 赋值0
为 AX 赋值0x4f00
执行 INT 0x10
若VBE存在,则AX会变为0x004f


********************************************************************

任务状态段TSS


struct TSS32{
//保存任务相关信息,任务切换的时候不会被写入(backlink有时会例外)
int backlink,esp0,ss0,esp1,ss1,esp2,ss2,cr3


//保存寄存器的设置
int eip,eflags,eax,ecx,edx,ebx,esp,ebp,esi,edi
int es,cs,ss,ds,fs,gs


//有关任务设置,与任务切换无关
//但是ldtr需要设置成0,iomap需要设置成0x40000000
int ldtr,iomap
}//104个字节


任务切换也是使用jmp,汇编格式与普通的jmp没有任何区别。
若一条jmp指令所指定的目标地址段不是可执行代码,而是TSS的话(去GDT中查看段的设置)
,CPU就会将这条指令理解为任务切换,cpu会切换到TSS中


对于课本286页TR的设置的原因(设置成3*8然后再进行任务切换)


TR保存的是,当发生任务切换时,当前执行任务的基本信息将会被存到哪个
TSS中,而TSS必须被注册到GDT中,且需要有一个号码.


set_segmdesc(gdt+3 , 103 , (int)&tss_a ,AR_TSS32);
set_segmdesc(gdt+4 , 103 , (int)&tss_b ,AR_TSS32);
对TR进行赋值,值为n号GDT与8的乘积
对4号TSS中的值进行设定,并将其eip指向tss_b_main
jmp 4*8


如以上设置,是将当前运行任务的基本信息以TSS的格式保存在3号GDT中,
然后将会切换到4号任务并执行


任务切换时若需要交换数据,可以指定一块内存,然后在另一个任务中读取内存,
也可以在4号任务的栈申请完成之后,将数据存入4栈。这样就可以避免
4号任务主函数的误操作带来的不一致。







tss的基本设定:
tss_a.ldtr = 0;
tss_a.iomap = 0x40000000;
tss_b.ldtr = 0;
tss_b.iomap = 0x40000000;
以下为tss_b的相应设定
task_b_esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024;
tss_b.eip = (int) &task_b_main;   //此处便是即将执行的任务
tss_b.eflags = 0x00000202; /* IF = 1; */
tss_b.eax = 0;
tss_b.ecx = 0;
tss_b.edx = 0;
tss_b.ebx = 0;
tss_b.esp = task_b_esp;
tss_b.ebp = 0;
tss_b.esi = 0;
tss_b.edi = 0;
tss_b.es = 1 * 8;
tss_b.cs = 2 * 8;
tss_b.ss = 1 * 8;
tss_b.ds = 1 * 8;
tss_b.fs = 1 * 8;
tss_b.gs = 1 * 8;


对于tss_b.cs指向2*8的原因:
通过查询GDT的2号段可知,GDT的2号段存储的是0x00280000-0x0028ffff,这
个段存储的是bookpack.hrb被加载段的信息。即tss_b与bookpack.hrb
位于同一个段。而查看bookpack.c可知task_b_main()确实位于一个段。
因此指定eip就可以将tss_b切换到4号任务上



#define MAX_TASKS1000//最大任务数量
#define TASK_GDT03//定义从GDT的几号开始分配给TSS
struct TSS32 {
int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
int es, cs, ss, ds, fs, gs;
int ldtr, iomap;
};
struct TASK {
int sel, flags;//sel用来存放GDT的编号
struct TSS32 tss;
};
struct TASKCTL {
int running;//正在运行任务的数量
int now;//这个变量用来记录当前运行的是哪个变量
struct TASK *tasks[MAX_TASKS];
struct TASK tasks0[MAX_TASKS];
};


********************************************************************

任务的优先级:本书的做法
将任务分成不同的级别level0-level9,每个任务都可能有n个任务,这n个任
务的优先级由prority判定。当leveli级别有任务在运行的时候,根据priority
判定优先级并执行。只有当leveli中的任务都休眠的时候,才会考虑执行
leveli+1的任务。这样可以保证高层任务绝对会快速响应


********************************************************************
文件的读取

struct FILEINFO{
unsigned char name[8],ext[3],type;
char reserve[10];
unsigned short time,date,clustno;
unsigned int size;
}
其中:
name8个字节文件名(不足8个用空格补齐)
ext3个字节文件扩展名
type1个字节文件属性信息
0x01只读文件
0x02隐藏文件
0x04系统文件
0x08非文件信息(如磁盘名称等)
0x10目录
reserve10个字节保留,为将来保存更多信息而预留
time2个字节存放文件的时间
date2个字节存放文件的日期
clustno2个字节代表这个文件内容从磁盘上的哪个扇区开始存放
size4个字节存放文件的大小




对于clustno:
磁盘映像中的地址 = clustno * 512 + 0x003e00 


但是对于大于512的文件,单靠以上做法是不行的:
FAT:file allocation table
从0柱面,0磁头,2扇区开始的9个扇区中
磁盘印象中相当于0x000200-0x0013ff
(它是有备份的:位置在0x001400-0x0025ff)
但是这个表是被加密过的,需要对他们进行解密:
对于每3个字节:
    如:03 04 00   -->   003 004
ab cd ef   -->   dab efc
解码之后,开始读取文件内容:
若文件大小小于512字节,则直接读取clustno对应的扇区。
若文件大小大于512字节,则先读取clustno对应的扇区,然后在fat中查找
对应的区段,之后进行接力查找(具体看P—384)


********************************************************************