《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.mzph.cn/news/633048.shtml

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

相关文章

jrebel IDEA 热部署

1 下载 2022.4.1 JRebel and XRebel - IntelliJ IDEs Plugin | Marketplace 2 选择下载好的zip 离线安装IDEA 插件 重启IDEA 3 打开 [Preference -> JRebel & XRebel] 菜单&#xff0c;输入 GUID address 为 https://jrebel.qekang.com/1e67ec1b-122f-4708-87d…

WINCC读写EXCEL-VBS

原创 RENHQ WINCC 关于VBS操作EXCEL的文档不管在论坛上还是在网上&#xff0c;相关的脚本已经很多&#xff0c;但是依然有很多人在问这个问题&#xff0c;于是把我以前在论坛上发的一个集合帖子的脚本拿来&#xff0c;重新开个帖子&#xff0c;如果再有人问的话&#xff0c;可…

使用动态sql时,if标签不起作用

目录 场景&#xff1a; 问题分析&#xff1a; 问题解决&#xff1a; 场景&#xff1a; 简单讲一下应用场景 我拿到一个项目&#xff0c;完成后端数据处理&#xff0c;在进行可选条件查询时&#xff0c;使用动态sql 在mapper.xml中我先是这么写的 <select id"list&…

数学建模--比赛

内容来自数学建模BOOM&#xff1a;【快速入门】北海&#xff1a;数模建模基础MATLAB入门论文写作数学模型与算法(推荐数模美赛国赛小白零基础必看教程)_哔哩哔哩_bilibili 目录 1.学习内容 2.参赛须知 1&#xff09;参赛作品的组成 2)参赛作品的提交 3.软件安装 4.注意…

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

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

新手入门Java第二阶段 封装概念及包、访问修饰符和static修饰符介绍

第三章 封装 课前回顾 1.带参方法如何定义 访问修饰符 返回值类型 方法名(参数列表){//形式参数列表}对象名.方法名(实参列表);2.方法的参数是如何传递的 在java中&#xff0c;方法参数的传递都是值传递。只是基本数据类型作为参数传递时&#xff0c;传递的是值的拷贝。引用…

移动web开发流式布局

1.0 移动端基础 1.1 浏览器现状 PC端常见浏览器&#xff1a;360浏览器、谷歌浏览器、火狐浏览器、QQ浏览器、百度浏览器、搜狗浏览器、IE浏览器。 内核&#xff1a; 浏览器内核备注Safariwebkitwebkit内核是苹果公司开发的一款渲染引擎&#xff0c;目前已被很多手机厂商所采…

UE5 蓝图编辑美化学习

虚幻引擎中干净整洁蓝图的15个提示_哔哩哔哩_bilibili 1.双击线段成节点。 好用&#xff0c;爱用 2.用序列节点 好用&#xff0c;爱用 3.用枚举。 好用&#xff0c;能避免一些的拼写错误 4.对齐节点 两点一水平线 5.节点上下贴节点 &#xff08;以前不懂&#xff0c;现在经常…

Gitlab添加ssh-key报500错误处理

Gitlab添加ssh-key报500错误 一、查看日志 发现Errno::Enoent(No such file or derectory -ssh): rootasu1:/home/caixin# tail -f /var/log/gitlab/gitlab-rails/production.log二、分析 根据日志提示&#xff0c;好像是缺少文件或目录&#xff0c;后面有个ssh,难首是依赖s…

C#winform上位机开发学习笔记1-串口助手的ModbusCRC功能

1.首先自定义CRC校验函数 private UInt16 Crc_Check(byte[] Data, byte DataLEN){UInt16 CRC 0xFFFF;for (byte i 0; i < DataLEN; i){ CRC ^ Data[i];for(byte j 0; j < 8; j){if((CRC & 0x0001) 0x0001){CRC (UInt16)((CRC >> 1) ^ 0xA001);}else{CRC …

SQL 最大连续合格次数 最大连胜记录次数 最大连败记录次数

有这样一个问题&#xff0c;工厂中要统计某个供应商送货检验的情况&#xff0c;依照其连续合格次数&#xff0c;决定是否免检&#xff0c;不使用游标或者循环&#xff0c;如何写这个sql。 此情景也可以用于统计连胜记录等 先要学习一下 窗函数LAG&#xff0c;指的是按分组和排…

vue 解决el-table 表体数据发生变化时,未重新渲染问题

效果图父组件中数量改变后总数重新计算 子组件完整代码 <template><el-tableshow-summaryref"multipleTable"v-bind"$props"selection-change"handleSelectionChange"row-click"handleRowClick":summary-method"getSum…

经典目标检测YOLO系列(二)YOLOV2的复现(2)正样本的匹配、损失函数的实现及模型训练

经典目标检测YOLO系列(二)YOLOV2的复现(2)正样本的匹配、损失函数的实现及模型训练 我们在之前实现YOLOv1的基础上&#xff0c;加入了先验框机制&#xff0c;快速的实现了YOLOv2的网络架构&#xff0c;并且实现了前向推理过程。 经典目标检测YOLO系列(二)YOLOV2的复现(1)总体…

【Go面试向】rune和byte类型的认识与使用

【Go】rune和byte类型的认识与使用 大家好 我是寸铁&#x1f44a; 总结了一篇rune和byte类型的认识与使用的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; byte和rune类型定义 byte,占用1个字节&#xff0c;共8个比特位&#xff0c;所以它实际上和uint8没什么本质区别,它表示…

Joern环境的安装(Windows版)

Joern环境的安装(Windows版) 网上很少有关于Windows下安装Joern的教程&#xff0c;而我最初使用也是装在Ubuntu虚拟机中&#xff0c;这样使用很占内存&#xff0c;影响体验感。在Windows下使用源码安装Joern也是非常简单的过程&#xff1a; 提前需要的本地环境&#xff1a; …

基于Java+SSM框架的办公用品管理系统详细设计和实现【附源码】

基于JavaSSM框架的办公用品管理系统详细设计和实现【附源码】 &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定…

GO 中如何防止 goroutine 泄露

文章目录 概述如何监控泄露一个简单的例子泄露情况分类chanel 引起的泄露发送不接收接收不发送nil channel真实的场景 传统同步机制MutexWaitGroup 总结参考资料 今天来简单谈谈&#xff0c;Go 如何防止 goroutine 泄露。 概述 Go 的并发模型与其他语言不同&#xff0c;虽说它…

蓝天采集器,功能逆天的网站数据抓取神器,轻松助你成为采集达人,附带搭建配置文档

源码介绍 蓝天采集器是一款专为web服务器打造的数据采集神器。与市面上常见的桌面端采集工具&#xff08;如火车头等&#xff09;相比&#xff0c;蓝天采集器在易用性、上手成本和灵活性方面更胜一筹。它部署简便&#xff0c;无需复杂的设置&#xff0c;即可迅速融入您的web服…

详解IP安全:IPSec协议簇 | AH协议 | ESP协议 | IKE协议_ipsec esp

目录 IP安全概述 IPSec协议簇 IPSec的实现方式 AH&#xff08;Authentication Header&#xff0c;认证头&#xff09; ESP&#xff08;Encapsulating Security Payload&#xff0c;封装安全载荷&#xff09; IKE&#xff08;Internet Key Exchange&#xff0c;因特网密钥…

storm统计服务开启zookeeper、kafka 、Storm(sasl认证)

部署storm统计服务开启zookeeper、kafka 、Storm&#xff08;sasl认证&#xff09; 当前测试验证结果&#xff1a; 单独配置zookeeper 支持acl 设置用户和密码&#xff0c;在storm不修改代码情况下和kafka支持当kafka 开启ACL时&#xff0c;storm 和ccod模块不清楚配置用户和密…