20天之前的一些笔记

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,1 1个扇区
MOV BX,0
MOV DL,0X00 A驱动器
MOV DL,0X00 调用磁盘BIOS
INT 0x13
JC  ERROR 错误则跳转到错误信息显示页面
********************************************************************
; 在系统进入32位模式之前, 将一些基本的信息存储以便方便使用

; 有关BOOT_INFO


CYLS EQU 0x0ff0 ; 设定启动区
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 有关颜色数目的信息,颜色的位数
SCRNX EQU 0x0ff4 ; 分辨率的X
SCRNY EQU 0x0ff6 ; 分辨率的Y
VRAM EQU 0x0ff8 ; 图像缓冲区的开始地址
; 对于INT 0x10,这种画面模式下VRAM是0xa0000-0xaffff


ORG 0xc200 ; 程序将要被放到的地方
MOV AL,0x13 ; VGA显卡,320x200x8bit位彩色
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ; 记录画面模式
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000


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


MOV AH,0x02
INT 0x16 ; 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.nas bookpack.c中使用的函数(C)的声明和定义。
hankaku.txt ASCII编码的点阵


//对于第六天的文件:
由于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_low 2字节
base_mid 1字节
base_high 1字节
段上限只有20位,剩余的4位要存储段属性。而这时段上限表示的最大范围是
1M,因此不合适。所以在段属性里面有一位Gbit位,若Gbit位1,limit的单
位不解释成byte,而是page,即4KB,这样20位的段上限就可以表示4G了
limit_low 2字节
limit_high 1字节(只有低四位表示段上限,高四位则表示段属性)


access_right 1字节(段属性的低八位在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=0x34 OUT(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_TASKS 1000 //最大任务数量
#define TASK_GDT0 3 //定义从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;
}
其中:
name 8个字节 文件名(不足8个用空格补齐)
ext 3个字节 文件扩展名
type 1个字节 文件属性信息
0x01 只读文件
0x02 隐藏文件
0x04 系统文件
0x08 非文件信息(如磁盘名称等)
0x10 目录
reserve 10个字节 保留,为将来保存更多信息而预留
time 2个字节 存放文件的时间
date 2个字节 存放文件的日期
clustno 2个字节 代表这个文件内容从磁盘上的哪个扇区开始存放
size 4个字节 存放文件的大小




对于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)


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













  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值