《30天自制操作系统》学习笔记(七)

先体验一下编译仿真方法:

30天自制操作系统光盘代码在下面链接,但是没有编译仿真工具:
https://gitee.com/zhanfei3000/30dayMakeOS

仿真工具在下面链接:
https://gitee.com/909854136/nask-code-ide

这是一个集成的编译仿真工具,只需要把上面仿真工具的文件夹:
\nask-code-ide-master\crtools
在这里插入图片描述
复制到源码文件加下,并改名为z_tools就可以按照书中的方法编译仿真了:
\30dayMakeOS-master\z_tools
z_tools如下:
在这里插入图片描述
在代码30dayMakeOS-master\01_day目录下执行下面编译仿真指令就可以看到仿真出的操作系统了。
在这里插入图片描述
在这里插入图片描述
后面章节源码写了 makefile就简单了,只要输入make就可以编辑了 然后再输入make run就可以仿真了

标题一、代码执行顺序(前内容六天的内容)

ipl10.nas–>asmhead.nas–>boopack.c

标题二、代码阅读

1.ipl10.nas(将软盘内容拷贝到内存中)

; haribote-ipl
; TAB=4
; 读取软盘内容到内存中,然后跳转到0xc200开始执行,就是asmhead.nas文件
CYLS	EQU		10				; CYLS=10 读取是10个柱面ORG		0x7c00			; 指明程序装载地址; 以下这段是FAT12格式软盘专用代码  0x7c00--0x7dff JMP        entryDB        0x90DB        "HARIBOTE"         ; 启动区的名字可以是任意的,但必须是8字节DW        512                ; 每个扇区(sector)的大小必须为512字节DB        1                  ;(cluster)的大小必须为1个扇区DW        1                  ; FAT的起始位置(一般从第一个扇区开始)DB        2                  ; FAT的个数(必须为2)DW        224                ; 根目录的大小(一般设为244项)DW        2880               ; 该磁盘的的大小(必须为2880扇区)DB        0xf0               ; 磁盘的种类(必须为0xfd)DW        9                  ; FAT的长度(必须为9扇区)DW        18                 ; 一个磁道(track)有几个扇区(必须为18)DW        2                  ; 磁头数(必须为2)DD        0                  ; 不使用分区(必须为0)DD        2880               ; 重写一次磁盘大小DB        0,0,0x29           ; 意义不明,固定DD        0xffffffff         ; (可能是)卷标号码DB        "HARIBOTEOS "      ; 磁盘名称(11字节)DB        "FAT12   "         ; 磁盘格式名称(8字节)RESB    18                   ; 先腾出18字节; 程序核心entry:MOV		AX,0			; AX=0 初始化寄存器MOV		SS,AX			; SS=AX=0MOV		SP,0x7c00		; SP=0x7c00MOV		DS,AX			; DS=AX=0; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  MOV		AX,0x0820		; AX=0x0820 设置缓存区的段地址MOV		ES,AX			; ES=AX=0x0820 ES:BX就是缓存区的地址MOV		CH,0			; CH=0 CH表示柱面号MOV		DH,0			; DH=0 DH表示磁头号MOV		CL,2			; CL=2 CL表示扇区号
readloop:MOV		SI,0			; SI=0, 用于记录错误次数,实现试错功能(非必须功能)
retry:MOV		AH,0x02			; AH=0x02 13号中断所需参数,表示操作类型,0x02(读盘),0x03写盘,0x04校验,0x0c寻道MOV		AL,1			; AL=1 AL处理对象的扇区数,表示一次只能读取1个扇区MOV		BX,0			; BX=0 缓冲地址MOV		DL,0x00			; DL=0x00 DL表示驱动器号INT		0x13			; BIOS提供的服务,用于操作软盘JNC		next			; CF=0,跳转到next执行ADD		SI,1			; SI=SI+1,记录尝试的次数,实现试错功能(非必须功能)CMP		SI,5			; JAE		error			; SI >= 5 跳转到error执行MOV		AH,0x00			; SI<5 AH=0x00 清空INT 0x13的错误码MOV		DL,0x00			; DL=0x00 设置驱动器号INT		0x13			; JMP		retry			; 跳转到retry执行
next:MOV		AX,ES			; AX=ESADD		AX,0x0020		; AX=AX+0x0020MOV		ES,AX			; ES=AX ES向后移动了一个扇区的大小ADD		CL,1			; CL=CL+1 扇区号加1CMP		CL,18			; JBE		readloop		; CL <= 18 跳转到readloop执行MOV		CL,1			; CL > 18 CL=1 ADD		DH,1			; DH=1 准备读取磁头0的内容CMP		DH,2			; JB		readloop		; DH < 2 跳转到readloop执行MOV		DH,0			; DH>=2 说明已读取完成ADD		CH,1			; CH=CH+1 准备读取下一个柱面CMP		CH,CYLS			; JB		readloop		; CH < CYLS 跳转到readloop执行; 磁盘内容装载内容的结束地址告诉haribote.sysMOV		[0x0ff0],CH		; [0x0ff0]=CH 将CYLS的值写入到内存地址0x0ff0中,可以参考asmhead.nas中对应的变量赋值JMP		0xc200			; 跳转到0xc200error:MOV		SI,msg			;SI=msg 显示错误信息
putloop:MOV		AL,[SI]			; AL=[SI] 读取[SI]内存中的信息ADD		SI,1			; SI=SI+1CMP		AL,0			; JE		fin				; AL==0, 错误信息显示完毕,跳转到finMOV		AH,0x0e			; AH=0x0e 设置显示属性MOV		BX,15			; BX=15 设置显示属性INT		0x10			; 调用BIOS显示服务JMP		putloop			; 
fin:HLT						; 让CPU停止等待命令JMP		fin				; 
msg:DB		0x0a, 0x0a		; DB		"load error"DB		0x0a			; DB		0				; RESB	0x7dfe-$		; DB		0x55, 0xaa		; 按规定设置字节

2.asmhead.nas(完成一些不能用c语言实现的功能,因为编码问题,有一些乱码,大概能看明白)

; haribote-os boot asm
; TAB=4[INSTRSET "i486p"]VBEMODE	EQU		0x105			; 1024 x  768 x 8bit 彩色
; 显示模式
;	0x100 :  640 x  400 x 8bit 彩色
;	0x101 :  640 x  480 x 8bit 彩色
;	0x103 :  800 x  600 x 8bit 彩色
;	0x105 : 1024 x  768 x 8bit 彩色
;	0x107 : 1280 x 1024 x 8bit 彩色BOTPAK	EQU		0x00280000		; 加载bootpack
DSKCAC	EQU		0x00100000		; 磁盘缓存的位置
DSKCAC0	EQU		0x00008000		; 磁盘缓存的位置(实模式); BOOT_INFO 相关
CYLS	EQU		0x0ff0			; 引导扇区设置
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			; 关于颜色的信息
SCRNX	EQU		0x0ff4			; 分辨率X
SCRNY	EQU		0x0ff6			; 分辨率Y
VRAM	EQU		0x0ff8			; 图像缓冲区的起始地址ORG		0xc200			;  这个的程序要被装载的内存地址; 确认VBE是否存在MOV		AX,0x9000MOV		ES,AXMOV		DI,0MOV		AX,0x4f00INT		0x10CMP		AX,0x004fJNE		scrn320; 检查VBE的版本MOV		AX,[ES:DI+4]CMP		AX,0x0200JB		scrn320			; if (AX < 0x0200) goto scrn320; 取得画面模式信息MOV		CX,VBEMODEMOV		AX,0x4f01INT		0x10CMP		AX,0x004fJNE		scrn320; 画面模式信息的确认CMP		BYTE [ES:DI+0x19],8		;颜色数必须为8JNE		scrn320CMP		BYTE [ES:DI+0x1b],4		;颜色的指定方法必须为4(4是调色板模式)JNE		scrn320MOV		AX,[ES:DI+0x00]				;模式属性bit7不是1就不能加上0x4000AND		AX,0x0080JZ		scrn320					; 模式属性的bit7是0,所以放弃;	画面设置MOV		BX,VBEMODE+0x4000MOV		AX,0x4f02INT		0x10MOV		BYTE [VMODE],8	; 屏幕的模式(参考C语言的引用)MOV		AX,[ES:DI+0x12]MOV		[SCRNX],AXMOV		AX,[ES:DI+0x14]MOV		[SCRNY],AXMOV		EAX,[ES:DI+0x28] ;VRAM的地址MOV		[VRAM],EAXJMP		keystatusscrn320:MOV		AL,0x13						; VGA图、320x200x8bit彩色MOV		AH,0x00INT		0x10MOV		BYTE [VMODE],8		; 记下画面模式(参考C语言)MOV		WORD [SCRNX],320MOV		WORD [SCRNY],200MOV		DWORD [VRAM],0x000a0000;	通过 BIOS 获取指示灯状态keystatus:MOV		AH,0x02INT		0x16 			; keyboard BIOSMOV		[LEDS],AL;	PIC关闭一切中断
;	根据AT兼容机的规格,如果要初始化PIC,
;	必须在CLI之前进行,否则有时会挂起。
;	随后进行PIC的初始化。MOV		AL,0xffOUT		0x21,ALNOP						; 如果连续执行OUT指令,有些机种会无法正常运行OUT		0xa1,ALCLI						; 禁止CPU级别的中断;	为了让CPU能够访问1MB以上的内存空间,设定A20GATECALL	waitkbdoutMOV		AL,0xd1OUT		0x64,ALCALL	waitkbdoutMOV		AL,0xdf			; enable A20OUT		0x60,ALCALL	waitkbdout;	切换到保护模式[INSTRSET "i486p"]				; 说明使用486指令LGDT	[GDTR0]			; 设置临时GDTMOV		EAX,CR0AND		EAX,0x7fffffff	; 设bit31为0(禁用分页)OR		EAX,0x00000001	; bit0到1转换(保护模式过渡)MOV		CR0,EAXJMP		pipelineflush
pipelineflush:MOV		AX,1*8			;  可读写的段 32bitMOV		DS,AXMOV		ES,AXMOV		FS,AXMOV		GS,AXMOV		SS,AX; bootpack传递MOV		ESI,bootpack	; 转送源MOV		EDI,BOTPAK		; 转送目标MOV		ECX,512*1024/4CALL	memcpy; 磁盘数据最终转送到它本来的位置去
; 首先从启动扇区开始MOV		ESI,0x7c00		; 转送源MOV		EDI,DSKCAC		; 转送目标MOV		ECX,512/4CALL	memcpy; 剩余的全部MOV		ESI,DSKCAC0+512	; 转送源MOV		EDI,DSKCAC+512	; 转送源目标MOV		ECX,0MOV		CL,BYTE [CYLS]IMUL	ECX,512*18*2/4	; 从柱面数变换为字节数/4SUB		ECX,512/4		; 减去 IPL 偏移量CALL	memcpy; 必须由asmhead来完成的工作,至此全部完毕
; 以后就交由bootpack来完成; bootpack启动MOV		EBX,BOTPAKMOV		ECX,[EBX+16]ADD		ECX,3			; ECX += 3;SHR		ECX,2			; ECX /= 4;JZ		skip			; 没有要转送的东西时MOV		ESI,[EBX+20]	; 转送源ADD		ESI,EBXMOV		EDI,[EBX+12]	; 转送目标CALL	memcpy
skip:MOV		ESP,[EBX+12]	; 堆栈的初始化JMP		DWORD 2*8:0x0000001bwaitkbdout:IN		 AL,0x64AND		 AL,0x02JNZ		waitkbdout	; AND的结果如果不是0,就跳到waitkbdoutRETmemcpy:MOV		EAX,[ESI]ADD		ESI,4MOV		[EDI],EAXADD		EDI,4SUB		ECX,1JNZ		memcpy			; 减法运算的结果如果不是0,就跳转到memcpyRET
; memcpy地址前缀大小ALIGNB	16
GDT0:RESB	8				; 初始值DW		0xffff,0x0000,0x9200,0x00cf	; 可以读写的段(segment)32bitDW		0xffff,0x0000,0x9a28,0x0047	; 可执行的文件的32bit寄存器(bootpack用)DW		0
GDTR0:DW		8*3-1DD		GDT0ALIGNB	16
bootpack:

3.bookpack.c(主函数文件,完成初始化等操作)

#include "bootpack.h"
#include <stdio.h>//该结构体用于控制鼠标
struct MOUSE_DEC {unsigned char buf[3], phase;	int x, y, btn;		
};extern struct FIFO8 keyfifo, mousefifo;	//外部变量,定义在fifo.c文件中
void enable_mouse(struct MOUSE_DEC *mdec);	//启动鼠标的函数
void init_keyboard(void);	//初始化键盘
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat);	//处理鼠标信息void HariMain(void)	//主函数
{struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;	//启动信息数据结构char s[40], mcursor[256], keybuf[32], mousebuf[128];	int mx, my, i;struct MOUSE_DEC mdec;init_gdtidt();	//初始化gdt.idtinit_pic();		//初始化picio_sti(); 	//执行STI指令fifo8_init(&keyfifo, 32, keybuf);	//初始化键盘缓存区	fifo8_init(&mousefifo, 128, mousebuf);	//初始化鼠标缓存区io_out8(PIC0_IMR, 0xf9); //设置中断io_out8(PIC1_IMR, 0xef); //因为键盘中断是IRQ1,鼠标中断时IRQ12,所以需要打开主从电路上的对应管脚init_keyboard();	//初始化键盘init_palette();	//初始化调色板init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);	//初始化屏幕,形成最初的窗口界面//获取画面中央的坐标mx = (binfo->scrnx - 16) / 2; my = (binfo->scrny - 28 - 16) / 2;init_mouse_cursor8(mcursor, COL8_008484);	//鼠标光标的显示putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);	sprintf(s, "(%3d, %3d)", mx, my);	//将鼠标位置转换成字符串putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);	//显示字符串,这个函数的位置在哪里?enable_mouse(&mdec);	//启动鼠标for (;;) {	io_cli();	//关闭中断	//如果键盘缓冲区和鼠标缓冲区中都没有数据if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {io_stihlt();	//打开中断并执行hlt命令} else {//如果键盘缓存区中有数据if (fifo8_status(&keyfifo) != 0) {i = fifo8_get(&keyfifo);	//从缓存区中读取数据(FIFO)sprintf(s, "%02X", i);	//将数据已字符串形式输出boxfill8(binfo->vram, binfo->scrnx, COL8_008484,  0, 16, 15, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);	} else if (fifo8_status(&mousefifo) != 0) {	//如果鼠标缓存区中有函数(鼠标和键盘的数据是怎么存入到对应缓存区的?)i = fifo8_get(&mousefifo);		//从缓存区中读取数据io_sti();	//打开中断if (mouse_decode(&mdec, i) != 0) {	//对鼠标信息进行处理sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);	if ((mdec.btn & 0x01) != 0) {	s[1] = 'L';}if ((mdec.btn & 0x02) != 0) {s[3] = 'R';}if ((mdec.btn & 0x04) != 0) {s[2] = 'C';}boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);boxfill8(binfo->vram, binfo->scrnx, COL8_008484, mx, my, mx + 15, my + 15); mx += mdec.x;my += mdec.y;if (mx < 0) {mx = 0;}if (my < 0) {my = 0;}if (mx > binfo->scrnx - 16) {mx = binfo->scrnx - 16;}if (my > binfo->scrny - 16) {my = binfo->scrny - 16;}sprintf(s, "(%3d, %3d)", mx, my);boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16); }}}}
}#define PORT_KEYDAT				0x0060
#define PORT_KEYSTA				0x0064
#define PORT_KEYCMD				0x0064
#define KEYSTA_SEND_NOTREADY	0x02
#define KEYCMD_WRITE_MODE		0x60
#define KBC_MODE				0x47//功能:等待键盘控制电路准备完毕
//如果键盘控制电路可以接受CPU指令,CPU从设备号码0x0064处所读取的数据倒数第二位应该是0,否则就是一直循环等待
void wait_KBC_sendready(void)
{for (;;) {if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) {	//判断第二位的情况break;}}return;
}//功能:初始化键盘
void init_keyboard(void)
{wait_KBC_sendready();	io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);wait_KBC_sendready();	io_out8(PORT_KEYDAT, KBC_MODE);	return;
}#define KEYCMD_SENDTO_MOUSE		0xd4
#define MOUSECMD_ENABLE			0xf4//功能:启用鼠标
void enable_mouse(struct MOUSE_DEC *mdec)
{wait_KBC_sendready();	io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);	wait_KBC_sendready();io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);mdec->phase = 0;return;
}//处理鼠标信息
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{if (mdec->phase == 0) {if (dat == 0xfa) {mdec->phase = 1;}return 0;}if (mdec->phase == 1) {if ((dat & 0xc8) == 0x08) {mdec->buf[0] = dat;mdec->phase = 2;}return 0;}if (mdec->phase == 2) {mdec->buf[1] = dat;mdec->phase = 3;return 0;}if (mdec->phase == 3) {mdec->buf[2] = dat;mdec->phase = 1;	 mdec->btn = mdec->buf[0] & 0x07;mdec->x = mdec->buf[1];mdec->y = mdec->buf[2];if ((mdec->buf[0] & 0x10) != 0) {mdec->x |= 0xffffff00;}if ((mdec->buf[0] & 0x20) != 0) {mdec->y |= 0xffffff00;}mdec->y = - mdec->y;return 1;}return -1; 
}

4.dsctbl.c(gdt和idt设置)

#include "bootpack.h"
//初始化gdt和idt
void init_gdtidt(void)
{struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;	//提前设置好的GDT在内存中的地址struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) ADR_IDT;	//提前设置好的IDT在内存中的地址int i;for (i = 0; i <= LIMIT_GDT / 8; i++) {	//对所有的全局描述符进行初始化set_segmdesc(gdt + i, 0, 0, 0);	//先将所有的全局描述符设置为0}set_segmdesc(gdt + 1, 0xffffffff,   0x00000000, AR_DATA32_RW);	//设置第1号描述符,段基址为0,大小4gb,可读写32位段set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER);	//设置第2号描述符,段基址0x00280000,大小0x0007ffff,属性0x4092load_gdtr(LIMIT_GDT, ADR_GDT);	//载入gdtr到cpu中for (i = 0; i <= LIMIT_IDT / 8; i++) {	//对所有的idt描述符进行初始化set_gatedesc(idt + i, 0, 0, 0);	//先将所有的idt描述符设为0}load_idtr(LIMIT_IDT, ADR_IDT);	//载入idtr到cpu中set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);	//对idt描述符赋值,注意第二个变量,是偏移量set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32);set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);return;
}//功能:对段描述符赋值
//参数:段描述符地址,长度、基址、属性值
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{//判断段描述大小的计数单位if (limit > 0xfffff) {	//如果段界限超过了限制1MBar |= 0x8000; /* G_bit = 1 */	//将G位设置为1,即大小以Kb为单位limit /= 0x1000;	//换算成KB}sd->limit_low    = limit & 0xffff;	//从低16位开始设置,即段界限的低16位sd->base_low     = base & 0xffff;	//接着设置16-31的16位数据,即基地址的低16位sd->base_mid     = (base >> 16) & 0xff;	//设置32-39的8位数据,即基地址的中间8位,将base右移16位,然后做与运算,取出中间8位sd->access_right = ar & 0xff;	//设置40-47位,即ar的第八位sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);	//设置49-56位,limit_high比较特殊,其中四位是段界限,4位是段属性值sd->base_high    = (base >> 24) & 0xff;	//设置57-63位,即段基址的高8位return;
}//功能:设置门描述符
//参数:门描述符地址,偏移、段选择符,属性值
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{gd->offset_low   = offset & 0xffff;	//设置0-15位,偏移地址的低16位gd->selector     = selector;	//设置16-31位,制度段选择子gd->dw_count     = (ar >> 8) & 0xff;	//设置32-39位,基本上全是0gd->access_right = ar & 0xff;	//设置40-47位,门描述符属性gd->offset_high  = (offset >> 16) & 0xffff;	//设置48-63Wie,偏移地址的高16位return;
}

5.graphic.c

//用于处理屏幕显示#include "bootpack.h"//初始化调色板
void init_palette(void)
{static unsigned char table_rgb[16 * 3] = {	//设置调色板变量,这里3个字符一组,组成了一个颜色,颜色应该是计算机中已经设定好的0x00, 0x00, 0x00,	0xff, 0x00, 0x00,	0x00, 0xff, 0x00,	0xff, 0xff, 0x00,	0x00, 0x00, 0xff,	0xff, 0x00, 0xff,	0x00, 0xff, 0xff,	0xff, 0xff, 0xff,	0xc6, 0xc6, 0xc6,	0x84, 0x00, 0x00,	0x00, 0x84, 0x00,	0x84, 0x84, 0x00,	0x00, 0x00, 0x84,	0x84, 0x00, 0x84,	0x00, 0x84, 0x84,	0x84, 0x84, 0x84	};set_palette(0, 15, table_rgb);	//设置调色板return;}//功能:设置调色板,将颜色和编号对上
void set_palette(int start, int end, unsigned char *rgb)
{int i, eflags;eflags = io_load_eflags();	//汇编语言函数io_cli(); //关闭中断					io_out8(0x03c8, start);	//写入端口for (i = start; i <= end; i++) {	//每三个一组合成一个rgb颜色io_out8(0x03c9, rgb[0] / 4);io_out8(0x03c9, rgb[1] / 4);io_out8(0x03c9, rgb[2] / 4);rgb += 3;}io_store_eflags(eflags);	//汇编语言函数return;
}//功能:画一个窗口
//其中xsize表示窗口宽度,理论上应该等于x1-x0
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{int x, y;for (y = y0; y <= y1; y++) {for (x = x0; x <= x1; x++)vram[y * xsize + x] = c;	//显示出字符}return;
}//初始化屏幕
void init_screen8(char *vram, int x, int y)
{boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);return;
}//显示字体
void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{int i;char *p, d /* data */;for (i = 0; i < 16; i++) {p = vram + (y + i) * xsize + x;	//显示版面的一行,此处应明白屏幕显示原理d = font[i];	//显示字体,按位显示if ((d & 0x80) != 0) { p[0] = c; }if ((d & 0x40) != 0) { p[1] = c; }if ((d & 0x20) != 0) { p[2] = c; }if ((d & 0x10) != 0) { p[3] = c; }if ((d & 0x08) != 0) { p[4] = c; }if ((d & 0x04) != 0) { p[5] = c; }if ((d & 0x02) != 0) { p[6] = c; }if ((d & 0x01) != 0) { p[7] = c; }}return;
}void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{extern char hankaku[4096];for (; *s != 0x00; s++) {putfont8(vram, xsize, x, y, c, hankaku + *s * 16);x += 8;}return;
}void init_mouse_cursor8(char *mouse, char bc)
{static char cursor[16][16] = {"**************..","*OOOOOOOOOOO*...","*OOOOOOOOOO*....","*OOOOOOOOO*.....","*OOOOOOOO*......","*OOOOOOO*.......","*OOOOOOO*.......","*OOOOOOOO*......","*OOOO**OOO*.....","*OOO*..*OOO*....","*OO*....*OOO*...","*O*......*OOO*..","**........*OOO*.","*..........*OOO*","............*OO*",".............***"};int x, y;for (y = 0; y < 16; y++) {for (x = 0; x < 16; x++) {if (cursor[y][x] == '*') {mouse[y * 16 + x] = COL8_000000;	//显示鼠标}if (cursor[y][x] == 'O') {mouse[y * 16 + x] = COL8_FFFFFF;}if (cursor[y][x] == '.') {mouse[y * 16 + x] = bc;}}}return;
}//功能:显示背景
//vram和vxsize是关于vram的信息
//pxsize,pysize是想要显示的图形大小
//px0、py0制定图像在画面上的显示位置
//buf指定图形存放的地址
//bxsize指定每一行含有的像素数
void putblock8_8(char *vram, int vxsize, int pxsize,int pysize, int px0, int py0, char *buf, int bxsize)
{int x, y;for (y = 0; y < pysize; y++) {for (x = 0; x < pxsize; x++) {vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x];}}return;
}

6.fifo.c

#include "bootpack.h"#define FLAGS_OVERRUN		0x0001//功能:初始化FIFO缓存区
//参数:缓存区结构体,大小,缓存区地址
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;	//将覆盖标志置1return -1;	//返回一个错误值}fifo->buf[fifo->p] = data;	//读取当前缓存区的第一个数据fifo->p++;	//将读取指针后移if (fifo->p == fifo->size) {	//如果已经读完缓存区fifo->p = 0;	//将读取指针指向第一个位置}fifo->free--;	//缓存区可用位置减1return 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++;	//可用区域加1 缓存区是循环写入的return data;
}int fifo8_status(struct FIFO8 *fifo)	//判断缓存区的状态
{return fifo->size - fifo->free;
}

7. int.c

#include "bootpack.h"
#include <stdio.h>//功能:中断初始化函数,初始化pic
void init_pic(void)
{io_out8(PIC0_IMR,  0xff  );	//主片禁止所有中断io_out8(PIC1_IMR,  0xff  ); //从片禁止所有中断//设置pic0,主片io_out8(PIC0_ICW1, 0x11  ); //边沿触发模式io_out8(PIC0_ICW2, 0x20  ); //IRQ0-7由INT20-27接收io_out8(PIC0_ICW3, 1 << 2); //PIC1由IRQ2接收io_out8(PIC0_ICW4, 0x01  );	//无缓冲区模式//设置pic1,从片io_out8(PIC1_ICW1, 0x11  );	//边沿触发模式io_out8(PIC1_ICW2, 0x28  );	//IRQ8-15由INT28-2f接收io_out8(PIC1_ICW3, 2     ); //PIC1由IRQ2连接io_out8(PIC1_ICW4, 0x01  );	//无缓冲区模式io_out8(PIC0_IMR,  0xfb  ); //11111011,PIC1以外的全部禁止io_out8(PIC1_IMR,  0xff  ); //11111111,禁止PIC1的所有中断return;
}#define PORT_KEYDAT		0x0060struct FIFO8 keyfifo;
//键盘中断处理,键盘是IRQ1,所以编写INT 0x21
//这里已经有了C语言编写的函数,为什么还要添加汇编语言的函数?
//是在汇编语言中调用该函数
void inthandler21(int *esp)
{unsigned char data;io_out8(PIC0_OCW2, 0x61);	data = io_in8(PORT_KEYDAT);	//读取数据fifo8_put(&keyfifo, data);	//将数据写入缓存区return;
}struct FIFO8 mousefifo;
//功能:鼠标中断处理,编写INT 0x2c
void inthandler2c(int *esp)
{unsigned char data;io_out8(PIC1_OCW2, 0x64);	io_out8(PIC0_OCW2, 0x62);	data = io_in8(PORT_KEYDAT);	//读取数据fifo8_put(&mousefifo, data);	//将数据写入缓存区return;
}void inthandler27(int *esp)								*/
{io_out8(PIC0_OCW2, 0x67); return;
}

8. naskfunc.nas(汇编和c语言文件之间的桥梁)

; naskfunc
; TAB=4[FORMAT "WCOFF"]				;
[INSTRSET "i486p"]				;
[BITS 32]						;
[FILE "naskfunc.nas"]			;
;定义外部符号,可以从文件外进行调用GLOBAL	_io_hlt, _io_cli, _io_sti, _io_stihltGLOBAL	_io_in8,  _io_in16,  _io_in32GLOBAL	_io_out8, _io_out16, _io_out32GLOBAL	_io_load_eflags, _io_store_eflagsGLOBAL	_load_gdtr, _load_idtrGLOBAL	_asm_inthandler21, _asm_inthandler27, _asm_inthandler2cEXTERN	_inthandler21, _inthandler27, _inthandler2c[SECTION .text]_io_hlt:	; void io_hlt(void);HLTRET_io_cli:	; void io_cli(void);CLIRET_io_sti:	; void io_sti(void);STIRET_io_stihlt:	; void io_stihlt(void);STIHLTRET_io_in8:	; int io_in8(int port); 从指定端口中读取数据MOV		EDX,[ESP+4]		; port,获取端口号MOV		EAX,0	;清空ax寄存器IN		AL,DX	;从DX指定的端口中读取数据到alRET_io_in16:	; int io_in16(int port);MOV		EDX,[ESP+4]		; portMOV		EAX,0IN		AX,DXRET_io_in32:	; int io_in32(int port);MOV		EDX,[ESP+4]		; portIN		EAX,DXRET_io_out8:	; void io_out8(int port, int data);	向指定端口写入数据MOV		EDX,[ESP+4]		; port	获取端口号MOV		AL,[ESP+8]		; data	获取要写入的数据OUT		DX,AL	;将al中的数据写入到dx指定的端口中RET_io_out16:	; void io_out16(int port, int data);MOV		EDX,[ESP+4]		; portMOV		EAX,[ESP+8]		; dataOUT		DX,AXRET_io_out32:	; void io_out32(int port, int data);MOV		EDX,[ESP+4]		; portMOV		EAX,[ESP+8]		; dataOUT		DX,EAXRET_io_load_eflags:	; int io_load_eflags(void);PUSHFD		; 将eflags寄存器压入栈中,入栈顺序是EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDIPOP		EAX	; 将edi的值弹出到eax中RET_io_store_eflags:	; void io_store_eflags(int eflags);MOV		EAX,[ESP+4]PUSH	EAXPOPFD		; 将栈中的寄存器值弹出到eflags寄存器中RET_load_gdtr:		; void load_gdtr(int limit, int addr);加载GDTR	MOV		AX,[ESP+4]		; limitMOV		[ESP+6],AXLGDT	[ESP+6]RET_load_idtr:		; void load_idtr(int limit, int addr); 记载IDTR,原理与加载GDTR相同MOV		AX,[ESP+4]		; limitMOV		[ESP+6],AXLIDT	[ESP+6]RET;这个函数只是将寄存器的值保存在栈里,然后将DS和ES调整到与SS相等,再调用_inthandler21,返回后将所有寄存器的值再返回到原来的值,然后执行IRETD
;之所以如此小心翼翼地保护寄存器,原因在于,中断处理发生在函数处理途中,通过IREDT从中断处理后,寄存器就乱了
_asm_inthandler21:PUSH	ESPUSH	DSPUSHAD	;PUSHAD指令压入32位寄存器,其入栈顺序是:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI .MOV		EAX,ESP	;eax=espPUSH	EAX	压入eax的值,即espMOV		AX,SS	MOV		DS,AX	;ds=ssMOV		ES,AX	;es=ssCALL	_inthandler21	;调用inthandler21函数,c语言编写POP		EAX	;弹出esp的值到eax中POPADPOP		DS	POP		ESIRETD_asm_inthandler27:PUSH	ESPUSH	DSPUSHADMOV		EAX,ESPPUSH	EAXMOV		AX,SSMOV		DS,AXMOV		ES,AXCALL	_inthandler27POP		EAXPOPADPOP		DSPOP		ESIRETD_asm_inthandler2c:PUSH	ESPUSH	DSPUSHADMOV		EAX,ESPPUSH	EAXMOV		AX,SSMOV		DS,AXMOV		ES,AXCALL	_inthandler2cPOP		EAXPOPADPOP		DSPOP		ESIRETD

标题三、其它

根据书中的描述,整个项目的编译过程如图
在这里插入图片描述

最后,asmhead文件和bookpack文件会编译到一起,bookpack的内容就是从bootpack标号开始?

关于中断处理程序的调用

如果要让一个中断处理程序发挥作用,首先要将其注册到idt中,书中使用了函数set_gatedesc(idt+0x21,(int)asm_inthandler21,2*8,AR_INTGATE32)2

即将_asm_inthandler21注册为idt的第21号,如果发生中断了,cpu就会自动调用asm_inthandler21

2表示asm_inthandler属于那一个段,因低3位必须是0,所以写成2*8
(ps:一定要读源码)
————————————————
版权声明:本文为CSDN博主「qq_35041101」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35041101/article/details/51866877

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/414410.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全全栈培训笔记(54-服务攻防-数据库安全RedisHadoopMysqla未授权访问RCE)

第54天 服务攻防-数据库安全&Redis&Hadoop&Mysqla&未授权访问&RCE 知识点&#xff1a; 1、服务攻防数据库类型安全 2、Redis&Hadoop&Mysql安全 3、Mysql-CVE-2012-2122漏洞 4、Hadoop-配置不当未授权三重奏&RCE漏洞 3、Redis-配置不当未授权…

隔夜挂单,快速交易通道,VIP柜台的极速体验!

许多中小散户抱怨&#xff0c;自己每天都在涨停板追这些新股&#xff0c;但从来没有买到过。相反&#xff0c;成交龙虎榜上&#xff0c;能买到秒停新股的常常是一些“涨停板敢死队”的老面孔。难道抢筹这些秒停、“一”字涨停的牛股有什么神器&#xff1f;追击秒停和“一”字板…

CBA对职业发展到底有多重要?速看!

&#x1f680;数字化转型&#xff0c;就像一场企业版的“跑男”&#xff0c;需要一个明确的“剧本”来指导每一步&#xff01;业务架构师就是这场大戏的“编剧”&#xff0c;他们运用业务架构原则和最佳实践&#xff0c;勾勒出完美的“剧情发展”&#xff0c;推动企业走向变革的…

树莓派4B +Ubuntu20.04+ROS1的使用(2)

首先确定一下主机与从机的ip地址&#xff08;非常重要&#xff09; 在这次实验中&#xff0c;主机是一台Ubuntu20.04.03系统的台式机&#xff0c;我们间通过这台准备来远程遥控树莓派上的ros1系统&#xff0c;它的ip地址是192.168.230.181 从机是一台搭载Ubuntu20.04桌面版ro…

数学建模.图论最短路径问题

一、概念&#xff08;本文是对清风网课的总结&#xff09; 推荐作图网站 有&#xff08;无&#xff09;向图的权重邻接矩阵 二、戴克斯特拉算法 缺点&#xff1a; 负权回路&#xff1a; 使用matlab计算最短路径 其他的一些操作 再次强调&#xff1a;本文是作者学习清风网课后的…

web蓝桥杯真题--10、灯的颜色变化

介绍 我们经常会看到各种颜色的灯光&#xff0c;本题我们将实现一个颜色会变化的灯的效果。 准备 开始答题前&#xff0c;需要先打开本题的项目代码文件夹&#xff0c;目录结构如下&#xff1a; ├── effect.gif ├── images │ ├── greenlight.svg │ ├── l…

滚动菜单+图片ListView

目录 Fruit.java FruitAdapter MainActivity activity_main.xml fruit.xml 整体结构 Fruit.java public class Fruit {private String name;private int imageId;public Fruit(String name, int imageId) {this.name name;this.imageId imageId;}public String getNam…

三菱plc学习入门(创建属于自己的FB模块)

在现实生活中&#xff0c;往往会需要修改一些属于方便自己的库&#xff0c;1&#xff0c;自己创建的库方便自己使用与查看2&#xff0c;提高自己编程能力&#xff0c;3&#xff0c;保护自己的程序不被外人修改&#xff01;&#xff01;&#xff01;下面就让我来操作一下 导入需…

【动态规划】【二分查找】【C++算法】730. 统计不同回文子序列

作者推荐 【动态规划】【数学】【C算法】18赛车 涉及知识点 动态规划 二分查找 LeetCode730. 统计不同回文子序列 给你一个字符串 s &#xff0c;返回 s 中不同的非空回文子序列个数 。由于答案可能很大&#xff0c;请返回对 109 7 取余 的结果。 字符串的子序列可以经由…

ChatGPT关键技术、发展方向与应用前景

源自&#xff1a;中国指挥与控制学会 作者&#xff1a;黄海涛 田虎 郑晓龙 曾大军 一、GPT类模型技术发展路径 图1 GPT类模型推出时间线1&#xff0c;2 二、ChatGPT模型的主要技术手段 图2 GPT类模型采用的Transformer解码器模型结构 图3 RLHF迭代训练过程真正推动ChatGPT模型…

Qt/C++自定义界面大全/20套精美皮肤/26套精美UI界面/一键换肤/自定义颜色/各种导航界面

一、前言 这个系列对应自定义控件大全&#xff0c;一个专注于控件的编写&#xff0c;一个专注于UI界面的编写&#xff0c;程序员有两大软肋&#xff0c;一个是忌讳别人说自己的程序很烂很多bug&#xff0c;一个就是不擅长UI&#xff0c;基本上配色就直接rgb&#xff0c;对于第…

新上线一个IT公司微信小程序

项目介绍 项目背景: 一家IT公司,业务包含以下六大块: 1、IT设备回收 2、IT设备租赁 3、IT设备销售 4、IT设备维修 5、IT外包 6、IT软件开发 通过小程序,提供在线下单,在线制单,在线销售,业务介绍,推广,会员 项目目的: 业务介绍: 包含企业业务介绍 客户需…