30天自制操作系统

os系统颜色模式及色号

Bit-深度 色彩数
1 2 (monochrome)
2 4 (CGA)
4 16 (EGA)
8 256 (VGA)
16 65,536 (High Color, XGA)
24 16,777,216 (True Color/真彩色, SVGA)
32 16,777,216 (True Color + Alpha Channel/控制透明度,-游戏特效)

os不同设备号码

常用设备对应端口号
常用协议对应端口号
常用服务对应端口号
端口号 服务名称和内容
21 文件传输协议的命令端口
22 SSH较安全的远程连接协议
23 Telnet 早期的连接服务器软件
25 SMTP邮件传输协议
53 DNS域名解析服务器
80 www服务器
110 pop3邮件接受协议
443 https有加密安全的www服务器
3306 mysql

BIOS内存分布图

这里写图片描述

MakeFile

一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作.

Makefile 文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建那些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。

make是一个命令工具,它解释Makefile 中的指令(应该说是规则)。

具体参考Makefile教程如:https://www.cnblogs.com/wang_yb/p/3990952.html
Makefile 文件

TOOLPATH = ../z_tools/
INCPAT`这里写代码片`H  = ../z_tools/haribote/

MAKE     = $(TOOLPATH)make.exe -r
NASK     = $(TOOLPATH)nask.exe
CC1      = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet
GAS2NASK = $(TOOLPATH)gas2nask.exe -a
OBJ2BIM  = $(TOOLPATH)obj2bim.exe
BIM2HRB  = $(TOOLPATH)bim2hrb.exe
RULEFILE = $(TOOLPATH)haribote/haribote.rul
EDIMG    = $(TOOLPATH)edimg.exe
IMGTOL   = $(TOOLPATH)imgtol.com
COPY     = copy
DEL      = del

# デフォルト動作

default :
	$(MAKE) img

# ファイル生成規則

ipl10.bin : ipl10.nas Makefile
	$(NASK) ipl10.nas ipl10.bin ipl10.lst

asmhead.bin : asmhead.nas Makefile
	$(NASK) asmhead.nas asmhead.bin asmhead.lst

bootpack.gas : bootpack.c Makefile
	$(CC1) -o bootpack.gas bootpack.c

bootpack.nas : bootpack.gas Makefile
	$(GAS2NASK) bootpack.gas bootpack.nas

bootpack.obj : bootpack.nas Makefile
	$(NASK) bootpack.nas bootpack.obj bootpack.lst

naskfunc.obj : naskfunc.nas Makefile
	$(NASK) naskfunc.nas naskfunc.obj naskfunc.lst

bootpack.bim : bootpack.obj naskfunc.obj Makefile
	$(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \
		bootpack.obj naskfunc.obj
# 3MB+64KB=3136KB

bootpack.hrb : bootpack.bim Makefile
	$(BIM2HRB) bootpack.bim bootpack.hrb 0

haribote.sys : asmhead.bin bootpack.hrb Makefile
	copy /B asmhead.bin+bootpack.hrb haribote.sys

haribote.img : ipl10.bin haribote.sys Makefile
	$(EDIMG)   imgin:../z_tools/fdimg0at.tek \
		wbinimg src:ipl10.bin len:512 from:0 to:0 \
		copy from:haribote.sys to:@: \
		imgout:haribote.img

# コマンド

img :
	$(MAKE) haribote.img

run :
	$(MAKE) img
	$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin
	$(MAKE) -C ../z_tools/qemu

install :
	$(MAKE) img
	$(IMGTOL) w a: haribote.img

clean :
	-$(DEL) *.bin
	-$(DEL) *.lst
	-$(DEL) *.gas
	-$(DEL) *.obj
	-$(DEL) bootpack.nas
	-$(DEL) bootpack.map
	-$(DEL) bootpack.bim
	-$(DEL) bootpack.hrb
	-$(DEL) haribote.sys

src_only :
	$(MAKE) clean
	-$(DEL) haribote.img

终端执行之后
这里写图片描述

day11
1.鼠标显示问题:图层跑到外面就会出现问题
2.实现画面外的支持:sheet_refreshsub函数把图层内容写入VRAM,让它不刷新新画面以外的部分。超出了画面则进行修正。
3.shtctl的指定省略:sheet_down函数不是很好,仅上下移动图层,就必须指定ctl,在struct SHEET中引入struct SHTCTL *ctl。
4.显示窗口:同制作背景和鼠标准备一张图层,在图层的缓冲区内描绘一个貌似窗口的图。
5.小实验:窗口图层放在最上面,光标图层放在其次
这里写图片描述
6.高速计数器:动作更丰富的窗口,能够计数,并将计数结果显示出来。

		count++;
		sprintf(s, "%010d", count);
		boxfill8(buf_win, 160, COL8_C6C6C6, 40, 28, 119, 43);
		putfonts8_asc(buf_win, 160, 40, 28, COL8_000000, s);
		sheet_refresh(sht_win, 40, 28, 120, 44);

不再采用HLT(不让CPU有空睡觉,全力计数,所以称为高速计数器)
出现问题:显示内容闪烁。
原因:刷新的时候,总是先刷新refresh范围内的背景图层,然后再刷新窗口图层。
7.消除闪烁(1):仅对refresh对象及其以上的图层进行刷新。
sheet_slide函数:图层的移动有时会导致下面的图层露出,所以从最下面开始刷新。另一方面,在移动目标处,比新移来的图层位置还要低的图层没有什么变化,而且只是隐藏起来了,所以只要刷新移动的图层和它上面的图层就可以了。
sheet_updown函数:同样思路,针对个别不需要自下而上全部刷新的部分只进行局部刷新。
8.消除闪烁(2):出现问题:鼠标开始闪烁
原因:是由于一会描绘一会消除造成的。
解决:在刷新窗口时避开鼠标所在的地方对VRAM进行写入处理。
首先,开辟一块内存,大小和VRAM一样(map)。这块内存用来表示画面上的点是哪个图层的像素。
这里写图片描述
这样,当刷新图层1的时候,可以一边看着map一边刷新,就不必担心图层1和图层2重叠的部分被覆盖了。
sheet_refreshmap同refreshsub函数基本一样,只是用色号代替了图层号码。
sheet_slide函数,首先要重写map,分别对应移动前后的图层,然后调用sheet_refreshsub函数。在移动前的地方,只针对上层图层移走之后的下层图层进行重绘就可以了,在移动目的地处仅重绘一张移动过去的图层。

day12定时器
1.使用定时器:只需对PIT(Programmable Interval Timer)可编程的间隔型定时器进行设定即可。
PIT连接着IRQ的0号,所以只要设定了PIT就可以设定IRQ0的中断间隔。
中断处理程序注册到IDT:
set_gatedesc(idt + 0x20, (int) asm_inthandler20, 2 * 8, AR_INTGATE32);
初始化(中断频率100HZ ):

	io_out8(PIT_CTRL, 0x34);
	io_out8(PIT_CNT0, 0x9c);
	io_out8(PIT_CNT0, 0x2e);

调用中断处理程序:

	io_out8(PIC0_OCW2, 0x60);//把IRQ-00信号接收完了的信息通知给PIC

2.计量时间:初始化PIT时,将计数变量置为0,每次发生定时器中断时,计数变量就以1递增。
3.超时功能:每次发生中断时,就把timeout-1,减到0时,就像fifo发送数据。
4.设定多个定时器:键盘和鼠标通过中断告诉有数据过来,但有些装置,即使状态有变化,也不会发生中断。所以只能定期去查询装置的状态。
5.加快中断处理(1):问题:每次进行定时器中断处理的时候,都会对所有活动中的定时器进行“timerctl.timer[i].timeout–”处理。
解决:改变程序变量timer[i].timeout的含义,指的不再是“所剩时间”而是“予定时刻”。现在时刻计数到timerctl.count中,拿它和timer[i].timeout进行比较,如果相同或是超过,就通过往FIFO缓冲区里传送数据通知HariMain。
6.加快中断处理(2):问题:每次中断都要执行多次中断
解决:到达下一个时刻时,检查是否超时,超时的话,就写入到FIFO中,没超时的话就检查是否将其设定为下一个时刻(未超时时刻中,最小的时刻是下一个时刻)
7.加快中断处理(3):改变程序结构

day13
1.简化字符串显示:将以下过程归纳到一个函数:先涂上背景色,再在上面写字符,最后完成刷新。
2.重新调整FIFO缓冲区(1):把定时器用的多个FIFO缓冲区集中成一个。只要在超时的情况下,往FIFO内写入不同的数据,就可以正常的分辨出是哪个定时器超时了。
3.测试性能:启动3秒后,将count置为0的原因,只要某些条件稍微有变化,电脑初始化所花费的时间就会有很大变化,这样做之后误差急剧减小。
4.重新调整FIFO缓冲区(2):把键盘和鼠标归纳起来
中断类型
0~1 光标闪烁用定时器
3 3秒定时器
10 10秒定时器
256~511 键盘输入(从键盘控制器读入的值再加上256)
512~767 鼠标输入(从键盘控制器读入的值再加512)
5.加快中断处理(4):面对多任务时,很多程序运行,每个应用程序都使用定时器,此时还使用移位处理的话,就有点浪费时间。
FIFO里取代处理移位的方法:读取一个数据以后不是让后面的数据向前靠齐,而是改变下一次的数据读取地址。不适用于定时器。
解决:在struct TIMER中加入next变量,用来存放下一个即将超时的定时器的地址。
6.使用哨兵简化程序:
4种可能:
运行中的定时器只有一个的情况
插入到最前面的情况
插入到s和t之间的情况
插入到最后面的情况
哨兵:将时刻oxffffffff定时器连到最后一个定时器上。(无论如何都不可能到达这个时刻,所以不可能发生超时问题)
加入哨兵后settime只存在两种情况
插入到最前面的情况
插入到s和t之间的情况

day14高分辨率与键盘输入
1.继续测试性能(使用大量定时器,进行性能比较)
2.提高分辨率(1):
高分辨率的利用方法因显卡不同而不同。
改写画面模式设定

		MOV		BX,0x4101		; VBE的640x480x8bit彩色
		MOV		AX,0x4f02

VBE的画面模式号码
0x101……640x 480x 8bit彩色
0x103……800x 600x 8bit彩色
0x105……1024x 768x 8bit彩色
0x107……1280x 1024x 8bit彩色
实际指定的时候,将以上的画面模式号码值加上0x4000,再复制到BX中去。
3.提高分辨率(2):VBE的版本不是2.0以上,就不能使用高分辨率。
画面模式信息中重要的6个信息
这里写图片描述
4.键盘输入(按下键时的数值表)
输入A值(if语句实现)
5.键盘输入(2):
输入字母和数字(字符数组实现)
6.追记内容(1):
窗口中添加了一些画,改变鼠标和字符的显示位置及颜色。按下退格键,改写已输入的字符
7.追记内容(2):
使用鼠标完成窗口移动

day15 多任务(1)
1.挑战任务切换:从任务A切换到任务B
CPU每次执行带有段地址的指令时,都会去确认一下GDT中的设置,以便判断接下来要执行的JMP指令到底是普通的far-JMP,还是任务切换。
如果一条JPMP指令所指定的目标地址段不是可执行的代码,而是TSS(任务状态段)的话,CPU就不会执行通常的改写EIP和CS的操作,而是将这条指令理解为任务切换。
A. 定义任务状态段

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;
};

B. 创建任务:

struct TSS32 tss_a, tss_b;

C. 向ldtr和iomap分别存入合适的值

	tss_a.ldtr = 0;
	tss_a.iomap = 0x40000000;
	tss_b.ldtr = 0;
	tss_b.iomap = 0x40000000;

D. 在GDT中定义

struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;
set_segmdesc(gdt + 3, 103, (int) &tss_a, AR_TSS32);
set_segmdesc(gdt + 4, 103, (int) &tss_b, AR_TSS32);

E. 定义寄存器的初始值

	tss_b.eip = (int) &task_b_main;   //切换到这个任务时,从哪里开始运行
	tss_b.eflags = 0x00000202; /* IF = 1; */   //把STI后的EFLAGS的值通过io_load_eflags赋给变量,变量的值就显示 0x00000202,所以这里直接使用了这个值
	tss_b.eax = 0;
	tss_b.ecx = 0;
	tss_b.edx = 0;
	tss_b.ebx = 0;
	tss_b.esp = task_b_esp;/*专门为任务b定义的栈,task_b_esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024;为任务b的栈分配了64KB的内存,并计算出栈底的内存地址*/
	tss_b.ebp = 0;
	tss_b.esi = 0;
	tss_b.edi = 0;
	tss_b.es = 1 * 8;//置为GDT的1号
	tss_b.cs = 2 * 8;//置为GDT的2号
	tss_b.ss = 1 * 8;
	tss_b.ds = 1 * 8;
	tss_b.fs = 1 * 8;
	tss_b.gs = 1 * 8;
	

F. 向TR寄存器存入3*8

load_tr(3 * 8);

作用让CPU记住当前正在运行哪一个任务。需要使用LTR指令,C语言做不到,只能把它写进naskfunc.nas

_load_tr:		; void load_tr(int tr);
		LTR		[ESP+4]			; tr
		RET

G. 任务切换(读取tss_b的内容)

taskswitch4();

执行far模式的跳转指令,C语言做不到,只能把它写进naskfunc.nas

_taskswitch4:	; void taskswitch4(void);
		JMP		4*8:0
		RET

用作任务切换的JMP指令,在切换任务之后,再返回这个任务的时候,程序会从这条JMP指令之后恢复运行,也就是执行JMP后面的RET,从 汇编语言函数返回,继续C语言主程序。
2. 任务切换进阶(2):再切回任务A,修改task_b_main函数,创建taskswitch3();
3. 做个简单的多任务(1):更快速的,来回交替的任务切换,告别光标停住、鼠标卡死、键盘打不了字等情况。
切换函数改写taskswitch4();——>farjmp(0,4*8);

_farjmp:		; void farjmp(int eip, int cs);
		JMP		FAR	[ESP+4]				; eip, cs
		RET

缩短切换间隔

//准备timer_ts变量,以便每隔0.02秒执行一次任务切换
//task_b_main中添加
if (i == 1) { /* 任务切换 */
				farjmp(0, 3 * 8);
				timer_settime(timer_ts, 2);
			}
//HariMain中添加
if (i == 2) {
				farjmp(0, 4 * 8);
				timer_settime(timer_ts, 2);
			}
  1. 做个简单的多任务(2)
    让task_b_main显示一些东西
    把变量的值从任务A传递给任务B:随便找个地址存进去,然后再从那里读出来
  2. 提高运行速度
    欺骗task_b_main
task_b_esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 8;
*((int *) (task_b_esp + 4)) = (int) sht_back;
  1. 测试运行速度
  2. 多任务进阶
    真正的多任务,是要做到在程序本身不知道的情况下进行任务切换。
    添加mtask.c
struct TIMER *mt_timer;
int mt_tr;

void mt_init(void)
{
	mt_timer = timer_alloc();
	/* timer_init偼昁梫側偄偺偱傗傜側偄 */
	timer_settime(mt_timer, 2);
	mt_tr = 3 * 8;
	return;
}

void mt_taskswitch(void)
{
	if (mt_tr == 3 * 8) {
		mt_tr = 4 * 8;
	} else {
		mt_tr = 3 * 8;
	}
	timer_settime(mt_timer, 2);
	farjmp(0, mt_tr);
	return;
}

day16 多任务(2)

  1. 任务管理自动化
    A.结构定义
#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];
};

B.对任务初始化(返回一个内存地址,代表正在运行程序,已经变成一个任务了)

struct TASK *task_init(struct MEMMAN *memman)
{
	int i;
	struct TASK *task;
	struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;
	taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));
	for (i = 0; i < MAX_TASKS; i++) {
		taskctl->tasks0[i].flags = 0;
		taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;
		set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);
	}
	task = task_alloc();
	task->flags = 2; /* 活动中标志 */
	taskctl->running = 1;
	taskctl->now = 0;
	taskctl->tasks[0] = task;
	load_tr(task->sel);
	task_timer = timer_alloc();
	timer_settime(task_timer, 2);
	return task;
}

C.初始化任务结构的函数

struct TASK *task_alloc(void)
{
	int i;
	struct TASK *task;
	for (i = 0; i < MAX_TASKS; i++) {
		if (taskctl->tasks0[i].flags == 0) {
			task = &taskctl->tasks0[i];
			task->flags = 1; /* 正在使用的标志 */
			task->tss.eflags = 0x00000202; /* IF = 1; */
			task->tss.eax = 0; /* 这里先置为0 */
			task->tss.ecx = 0;
			task->tss.edx = 0;
			task->tss.ebx = 0;
			task->tss.ebp = 0;
			task->tss.esi = 0;
			task->tss.edi = 0;
			task->tss.es = 0;
			task->tss.ds = 0;
			task->tss.fs = 0;
			task->tss.gs = 0;
			task->tss.ldtr = 0;
			task->tss.iomap = 0x40000000;
			return task;
		}
	}
	return 0; /* 全部正在使用 */
}

D.将task添加到tasks的末尾,然后使running加1

void task_run(struct TASK *task)
{
	task->flags = 2; /* 活动中标志 */
	taskctl->tasks[taskctl->running] = task;
	taskctl->running++;
	return;
}

E.切换函数(当running为1时,不需要进行任务切换函数直接结束,当running大于等于2时,先把now加1,然后把now所代表的任务切换成当前任务,最后再将末尾的任务移动到开头。)

void task_switch(void)
{
	timer_settime(task_timer, 2);
	if (taskctl->running >= 2) {
		taskctl->now++;
		if (taskctl->now == taskctl->running) {
			taskctl->now = 0;
		}
		farjmp(0, taskctl->tasks[taskctl->now]->sel);
	}
	return;
}
  1. 让任务休眠
    休眠:将一个任务从tasks中删除的操作
    唤醒:运行task_run(需要先确认该任务是否处于休眠的状态,然后再将其唤醒)
  2. 增加窗口数量
    为系统增加更多任务,各自拥有自己的窗口
  3. 设定任务优先级(1)
    通过参数设定优先级
  4. 设定任务优先级(2)
    这里写图片描述
    原理:最上层的LEVEL0只要存在哪怕一个任务,则完全忽略LEVEL1和LEVEL2中的任务,只在LEVEL0的任务中进行任务切换。当LEVEL0中的任务全部休眠,或者全部降到下层LEVEL,也就是当LEVEL0没有任何任务的时候,接下来开始轮到LEVEL1中的任务进行任务切换。当LEVEL0和LEVEL1中都没有任务的时候,就该轮到LEVEL2的出场。

day17 命令行窗口

  1. 闲置任务:所有LEVEL中都没有任务时会出现问题。解决:采用哨兵思路。将闲置任务放在最下层LEVEL中。
  2. 创建行窗口:单独做成一个新的任务
  3. 切换输入窗口:让系统在按下“Tab”键的时候,将输入窗口切换到命令行窗口。(改变窗口颜色)引入key_to变量,用于记录键盘输入(key)应该发送到(to)哪里。为0则发送到任务A,为1则发送到命令行窗口任务。
  4. 实现字符输入:将struct FIFO与struct TASK绑定起来(由于在命令行窗口使用了定时器,为了不与键盘数据冲突,在写入FIFO时将键盘的数据的值加上256.问题:无法输入“!”和“%”
  5. 符号的输入: 实现输入“!”和“%”,处理shift键
    这里写图片描述
    程序原理:先将按键 编码转换成字符编码,转换结果存入s[0],如果遇到无法转换的按键编码,则向s[0]存入0.
  6. 大写字母和小写字母:同时判断shift键和CapsLock键的状态
    在asmhead.nas中,已经从BIOS中获得键盘的状态,保存在binfo->leds中
    这里写图片描述
    问题:模式可以切换,但指示灯没有发生变化。
  7. 对各种锁定键的支持:
    原理:创建keycmd的FIFO缓冲区(管理由任务向键盘控制器发送数据的状态)
    keycmd_wait(表示向键盘控制器发送数据的状态)

day18 dir命令
1.控制光标闪烁(1):只有接受键盘输入的窗口有光标闪烁,其他窗口是不显示光标的。(不想显示光标的时候,将cursor_c的值为负)
2. 控制光标闪烁(2):由任务A向conslose(命令行窗口)传递信息,通过FIFO实现。光标开始闪烁定义为2,光标停止闪烁定义为3.
3. 对回车键的支持:问题:命令行窗口按下回车键无响应。
按下回车键向命令行窗口发送10+256这个值。
4. 对窗口滚动的支持:问题:到最后一行的时候回车键就不管用了。
实现窗口滚动:将所有的像素向上移动一行就可以了。移动完成之后,还需要将最后一行的内容涂黑,否则上面一行的内容会残留在那里。
5. mem命令:显示内存使用情况
去掉显示内容,使用命令查询。
6. cls命令: 清屏命令
7. dir命令:显示文件名、文件日期、文件大小
借助BIOS
数据存放位置
这里写图片描述
文件名位置
这里写图片描述
day19 应用程序

  1. type 命令(获取文件内容)
    磁盘映像中的地址=clusno*512+0x003e00
    判断命令—准备文件名—寻找文件—显示指定文件内容(未找到文件,提示错误信息)
    问题 :出现乱码(忘记处理换行和制表符的的字符编码了)
  2. type命令改良:
    0x09……制表符:显示空格直到x被4整除为止
    0x0a……换行符:换行
    0x0d……回车符:代表让打印头回到行首
    问题:可以正确显示文件开头的512个字节内容,但是遇到 大于512个字节的文件,中间可能就会突然显示出其他文件的内容。
  3. 对FAT的支持(记录文件在磁盘中存放位置的表)
    文件下一段存放在哪,磁盘中是有记录的,分析这个记录,就可以正确读取文件内容。位于从0柱面、0磁头、2扇区开始的9个扇区中,在磁盘映像中相当于0x000200~0x0013ff。
    解压缩—读取扇区内容(接力方式)
    接力方式下,只要其中一个地方损坏,之后的部分就会全部混乱。为此在磁盘中存了两份FAT,第一份FAT位于0x0002000x0013ff,第二份位于0x0014000x0025ff
  4. 代码整理:
    窗口相关函数——window.c
    命令行窗口相关函数——console.c
    文件相关函数——file.c
  5. 第一个应用程序
    day20 API
  6. 程序整理:
  7. 显示单个字符的API(1):
    CALL指令(调用函数的指令):为了在执行RET指令时正确返回,会先将要返回的目标地址PUSH到栈中。
    问题:qemu.exe出错关闭。
  8. 显示单个的字符(2):
    应用程序对API执行CALL的时候,千万不能忘记加上段号
    问题:模拟器qemu.exe停止响应了
    普通的RET指令只是用于普通的CALL的返回,既然这里用来far-CALL,就必须相应的使用far-RET,也就是RETF。
[BITS 32]
		MOV		AL,'A'
		CALL    2*8:0xbe3
fin:
		HLT
		JMP		fin

4.结束应用程序:应用程序结束后不执行HLT,而是返回操作系统。
创建farcall函数—hlt命令改为调用farcall—改写hlt.nas(HLT换成RETF)
出现问题:qemu.exe停止响应了
原因:改写了操作系统代码,导致_asm_cons_putcahr的地址发生改变
5. 不随操作系统版本而改变的API:操作系统版本改变,应用程序照样运行
注册函数(中断处理的程序):

set_gatedesc(idt + 0x40, (int) asm_cons_putchar, 2 * 8, AR_INTGATE32);
  1. 为应用程序自由命名:
    去掉cmd_hlt,创建cmd_app(用来根据命令行的内容判断文件名,并运行相应的应用程序)
    工作过程:当输入的命令不是mem、cls、dir、type其中之一时,则调用cmd_app。如果返回0则作为错误处理。
    7.当心寄存器:
    给_asm_cons_putcahr加上PUSHAD和POPAD
    原因:INT0x40之后ECX寄存器的值发生了变化所导致的,应该是_cons_putchar改动了ECX的值。因此加上PUSHAD和POPAD 确保可以将全部寄存器的值还原,这样程序就能正常运行了。
    8.用API显示字符串:
    两种方式:
    1.显示一串字符串,遇到字符编码0则结束
    2.先指定好要显示的字符串的长度再显示
    问题:hello.hrb运行正常,hello.hrb2出现异常
    原因:内存段的问题。显示单个字符时,用[CS:ECX]的方式特意指定了CS(代码段寄存器),因此可以成功读取msg的内容。但在显示字符串的时候,由于无法指定段地址,程序误以为是DS而从完全错误的内存地址中读取了内容,碰巧读出的内容是0,于是什么都没有显示出来
    day21 保护操作系统
  2. 攻克难题—字符串显示API:
    改动:能够将应用程序传递的地址解释为代码段内的地址。
  3. 用C语言编写应用程序
  4. 保护操作系统(1):
    crack1(擅自操作了本该由操作系统来管理的内存空间)
*((char *) 0x00102600) = 0;

这里写图片描述
4.保护操作系统(2):
5. 对异常的支持:(强制结束程序,在中断号0x0d中注册一个函数)
当应用程序师徒破坏操作系统,或者违背操作系统的设置时,就会自动产生ox0d中断。
6. 保护操作系统(3):擅自向DS存入了操作系统用的段地址

[INSTRSET "i486p"]
[BITS 32]
		MOV		EAX,1*8			; OS用的段号
		MOV		DS,AX			; 将其存入DS
		MOV		BYTE [0x102600],0
		RETF

7.保护操作系统(4):
在段定义的地方,如果将访问权限加上0x60的话,就可以将段设置为应用程序用

day22 用C语言编写应用程序
1.保护操作系统(5):在定时器上动手脚
2.帮助发现Bug:
产生异常时其他寄存器的值
这里写图片描述
3. 强制结束应用程序:应用组合键设置强制结束键
工作原理:当按下强制结束键时,改写命令窗口任务的寄存器值,并goto到asm_end_app
4. 用C语言显示字符串(1):
5. 用C语言显示字符串(2):
.hrb文件开头的36个字节不是程序存放了以下内容:
这里写图片描述
6. 显示窗口:
这里写图片描述
7. 在窗口中描绘字符和方块:
这里写图片描述
day23 图形处理相关
1.编写malloc
这里写图片描述
2. 画点
这里写图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值