16.代码整理

简介

操作系统开发到目前可以说已经掌握了相当多的知识体系了,是时候对相关代码作完善、美化的时候了,在开发中笔者发现了一些潜在的bug,正好作一些修复。以前都是农耕时代,现在将进入工业化生产阶段。

目标

1.代码功能模块化、扩展性强;
2.代码最好的注释就是代码(变量、函数名等见名知意);
3.隐藏该隐藏的,暴露该暴露的;
4.以后代码的添加将只会介绍新增的代码,避免大量重复的长篇博客;
5.每一文章末尾都会有整个文章的最终代码;
6.文中所有代码笔者都愿意把自己的思考开源给任何人使用,无需向笔者申请

说明

1.boot.s

;能用于操作内存的寄存器只能是bx、bp、si、di、sp,其中不指定段寄存器时bp、sp默认使用ss,其余使用ds
;0x7c00--0x7dff 这512字节用于启动区
;对内存的访问都必须指定段寄存器,没有显示指定时将使用ds作为段寄存器


    org 0x7c00

    LOAD_ADDR EQU 0x9000   ;内核加载偏移地址

    mov ax,0
    mov ss,ax
    mov ds,ax
    mov es,ax 

    mov bx,LOAD_ADDR
   
    mov ch,1        ;柱面号
    mov dh,0        ;磁头号
    mov cl,1        ;扇区号
    mov ah,0x02     ;0x02表示读盘操作
    mov al,18       ;表示连续读取扇区数
    mov dl,0        ;驱动器号,早期有多个软驱,一般只有一个写死0
    int 0x13        ;调用BIOS实现磁盘读取
    jc error        ;读盘操作失败,flag标志寄存器cf 标志位被置1 
    jmp bx          ;跳转到加载的内存地址开始执行代码


fin:
    hlt
    jmp fin


putloop:
    mov al,[si]
    inc si
    cmp al,0
    je fin
    mov ah,0x0e     ;中断调用参数
    mov bx,15       ;字符颜色
    int 0x10        ;中断调用号
    jmp putloop


error:
    mov si,errMsg
    jmp putloop
   

errMsg:
    db 'load floppy error'
    db 0

2.floppy.h

//
//  floppy.h
//  os-test
//
//  Created by vincent on 2018/10/29.
//  Copyright © 2018年 vincent. All rights reserved.
//

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

/**
 *根据文件名创建一个3.5寸虚拟软盘
 *3.5寸软盘结构:80个磁道,2个磁头,18个扇区。每个扇区512字节
 *系统读取磁盘结构:0磁道0磁头1扇区,0磁道0磁头2扇区...,0磁道0磁头18扇区,0磁道1磁头1扇区,
 *0磁道1磁头2扇区...,0磁道1磁头18扇区。再从1磁道0磁头1扇区....循环读取操作
 */
FILE* initFloppy(char *fileName);


/**
 *读取软盘指定磁道,磁头,扇区数据
 *@param    c       磁道[0,79]
 *@param    h       磁头[0,1]
 *@param    s       扇区[1,18]
 *@param    fp      初始化的虚拟软盘文件
 *@param    buf     读取的磁盘数据,大小长度至少应该是512字节
 */
void readFloppy(int c,int h,int s,FILE *fp,char *buf);


/**
 *写入软盘指定磁道,磁头,扇区数据
 *@param    c       磁道[0,79]
 *@param    h       磁头[0,1]
 *@param    s       扇区[1,18]
 *@param    fp      初始化的虚拟软盘文件
 *@param    buf     读取的磁盘数据,大小长度至多是512字节
 */
void writeFloppy(int c,int h,int s,FILE *fp,char *buf);

3.floppy.c

//
//  floppy.c
//  os-test
//
//  Created by vincent on 2018/10/29.
//  Copyright © 2018年 vincent. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


FILE* initFloppy(char *fileName){
    FILE *fp = fopen(fileName, "w");
    
    char buf[512];
    memset(buf, 0, 512);
    for(int c=0;c<80;c++){//磁道
        for(int h=0;h<2;h++){//磁头
            for(int s=1;s<=18;s++){//扇区
                fwrite(buf, 512, 1, fp);
            }
        }
    }
    return fp;
}


void readFloppy(int c,int h,int s,FILE *fp,char *buf){
    //把虚拟软盘映射成线性偏移
    int index = 18*2*512*c + h*18*512 + (s-1)*512;
    
    int tmp = (int)ftell(fp);
    fseek(fp, index, SEEK_SET);
    fread(buf,512,1,fp);
    fseek(fp, tmp, SEEK_SET);
}


void writeFloppy(int c,int h,int s,FILE *fp,char *buf){
    
    //把虚拟软盘映射成线性偏移
    int index = 18*2*512*c + h*18*512 + (s-1)*512;
    
    int tmp = (int)ftell(fp);
    fseek(fp, index, SEEK_SET);
    fwrite(buf, 512, 1, fp);
    fseek(fp, tmp, SEEK_SET);
}

4.main.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#include "floppy.h"


int main(int argc,char **argv){
    
    FILE *fp = initFloppy("floppy.img");
    if(fp == NULL) {
        printf("初始化磁盘失败");
        exit(0);
    }

    FILE *src = fopen("boot.bat", "r");
    if(src == NULL) {
        printf("文件打开失败:%s\n","boot.bat");
        exit(0);
    }
    char buf[512];
    memset(buf, 0, 512);
    fread(buf, 512, 1, src);
    buf[510] = 0x55;
    buf[511] = 0xaa;
    writeFloppy(0, 0, 1, fp, buf);
    fclose(src);


    src = fopen("kernel.bat", "r");
    if(src == NULL) {
        printf("文件打开失败:%s\n","kernel.bat");
        exit(0);
    }
    
    //可正确处理18个扇区的数据内容
	int c = 1,h = 0,s = 1;
	int count = 0;
    for(int i=0;!feof(src) && i < 18;i++){
        memset(buf, 0, 512);

        fread(buf, 512, 1, src);
        writeFloppy(c, h,s, fp, buf);
		count += 1;
    	s++;
		
		if(s==19){
			h++;
			s = 1;
		}
		if(h==2){
			h = 0;
			c++;
		}	
    }
	
	fseek(src,0,SEEK_END);
	long len = ftell(src);
	if(len > 18*512){
		printf("kernel.bat 文件长度%ldB,超过写入界限!!!\n",len);
	}
	else{
		printf("读取了%dx512=%dB,floppy.img软盘文件制作成功\n",count,count*512);
	}

	
    fclose(src);

    fclose(fp);
    return 1;
}

5.kernel.s

    ;全局描述符结构 8字节
    ; byte7 byte6 byte5 byte4 byte3 byte2 byte1 byte0
    ; byte6低四位和 byte1 byte0 表示段偏移上限
    ; byte7 byte4 byte3 byte2 表示段基址
    


    ;定义全局描述符数据结构
    ;3 表示有3个参数分别用 %1、%2、%3引用参数
    ;%1:段基址     %2:段偏移上限  %3:段属性
    %macro GDescriptor  3
        dw %2 & 0xffff
        dw %1 & 0xffff
        db (%1>>16) & 0xff 
        dw ((%2>>8) & 0x0f00) | (%3 & 0xf0ff)
        db (%1>>24) & 0xff 
    %endmacro
    DA_32       EQU 4000h   ; 32 位段
    DA_C        EQU 98h ; 存在的只执行代码段属性值
    DA_DRW      EQU 92h ; 存在的可读写数据段属性值
    DA_DRWA     EQU 93h ; 存在的已访问可读写数据段类型值


	;中断描述符表
	;Gate selecotor, offset, DCount, Attr
	%macro Gate 4
		dw  (%2 & 0xffff)
		dw  %1
		dw  (%3 & 0x1f) | ((%4 << 8) & 0xff00)
		dw  ((%2>>16) & 0xffff)
	%endmacro
	DA_386IGate EQU 8Eh ; 中断调用门

   
    org 0x9000 
    jmp entry
    
    [SECTION .gdt]
    ;定义全局描述符                                段基址           段偏移上限       段属性
    LABEL_GDT:              GDescriptor         0,              0,             0
    LABEL_DESC_CODE:        GDescriptor         0,              SegCodeLen-1,  DA_C+DA_32 
    LABEL_DESC_VIDEO:       GDescriptor         0xb8000,        0xffff,        DA_DRW
    LABEL_DESC_STACK:       GDescriptor         0,              STACK_TOP-1,   DA_DRWA+DA_32
    LABEL_DESC_VRAM:        GDescriptor         0,              0xffffffff,    DA_DRW

    ;gdt 表大小
    GdtLen  equ     $-LABEL_GDT

    ;gdt表偏移上限和基地址
    GdtPtr  dw      GdtLen-1
            dd      0


    ;cpu开机进入实模式时使用的段寄存器 cs,ds,es,ss 和偏移地址组成内存地址,内存地址=段寄存器 * 16 + 偏移地址 
    ;保护模式中段寄存器保存的是gdt 描述表中各个描述符的偏移,也叫选择子
    

    SelectorCode32      EQU     LABEL_DESC_CODE-LABEL_GDT
    SelectorVideo       EQU     LABEL_DESC_VIDEO-LABEL_GDT
    SelectorStack       EQU     LABEL_DESC_STACK-LABEL_GDT
    SelectorVRAM        EQU     LABEL_DESC_VRAM-LABEL_GDT


	;中断描述符表
	LABEL_IDT:
	%rep  0x21
		Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate
	%endrep
	
	;键盘中断向量(8259A 键盘中断向量0x20,IRQ1 是键盘中断请求,0x20 + IRQ[n] = 0x21
	.0x21:
		Gate  SelectorCode32, KeyboardHandler,0, DA_386IGate
	
	%rep  10
		Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate
	%endrep
	
	;从中断控制器8259A 中断向量0x28,IRQ4 是鼠标中断请求,0x28 + IRQ[n] = 0x2c
	.0x2c:
		Gate  SelectorCode32, MouseHandler,0, DA_386IGate


	IdtLen  equ $ - LABEL_IDT
	IdtPtr  dw  IdtLen - 1
		    dd  0

    [SECTION .s16]
    [BITS 16]
entry:
    mov ax,cs 
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov sp,0x100 

	;检测系统内存
	 mov   ebx, 0
     mov   di, LABEL_MEM_BUF
LABEL_MEM_CHK_LOOP:
	 mov   eax, 0xE820
     mov   ecx, 20
     mov   edx, 0x534D4150
     int   0x15
     jc    LABEL_MEM_CHK_FAIL
     add   di, 20   
     inc   dword [LABEL_MEM_COUNT]
     cmp   ebx, 0
     jne   LABEL_MEM_CHK_LOOP
     jmp   LABEL_MEM_CHK_OK

LABEL_MEM_CHK_FAIL:
    mov    dword [LABEL_MEM_COUNT], 0

LABEL_MEM_CHK_OK:

    ;设置屏幕色彩模式
    mov al,0x13
    mov ah,0
    int 0x10

    ;设置LABEL_DESC_CODE描述符段基址
    mov eax,0 
    mov ax,cs 
    shl eax,4
    add eax,SEG_CODE32
    mov word [LABEL_DESC_CODE+2],ax
    shr eax,16
    mov [LABEL_DESC_CODE+4],al
    mov [LABEL_DESC_CODE+7],ah


    ;设置栈空间
    xor eax,eax
    mov ax,cs 
    shl eax,4
    add eax,LABEL_STACK
    mov word [LABEL_DESC_STACK+2],ax
    shr eax,16
    mov byte [LABEL_DESC_STACK+4],al
    mov byte [LABEL_DESC_STACK+7],ah


    mov eax,0
    mov ax,ds
    shl eax,4 
    add eax,LABEL_GDT
    mov dword [GdtPtr+2],eax

    ;设置GDTR 寄存器
    lgdt [GdtPtr]


    cli     ;关闭可可屏蔽中断,如键盘中断

    in al,0x92 
    or al,0x02
    out 0x92,al 

    mov eax,cr0
    or eax,1 
    mov cr0,eax


	
	call init8259A

	;加载中断描述符
	xor   eax, eax
	mov   ax,  ds
	shl   eax, 4
	add   eax, LABEL_IDT
	mov   dword [IdtPtr + 2], eax
	lidt  [IdtPtr]

	
	sti		;恢复中断
    jmp dword SelectorCode32:0



;初始化8259A中断控制器
init8259A:
	mov  al, 0x11		;向主8259A发送ICW1
	out  0x20, al
	call io_delay

	out 0xa0, al		;向从8259A发送ICW1
	call io_delay


	;20h 分解成ICW2 是, ICW2[0,1,2] = 0, 这是强制要求的,
	;也就是ICW2的值不能是0x21,0x22之类,只要前三位是0就行
	;整个ICW2 = 0x20,这样的话,当主8259A对应的IRQ0管线向CPU发送信号时,
	;CPU根据0x20这个值去查找要执行的代码,IRQ1管线向CPU发送信号时,
	;CPU根据0x21这个值去查找要执行的代码,依次类推

	mov al, 0x20		;向主8259A发送ICW2
	out 0x21, al		;
						;
	call io_delay

	mov  al, 0x28		;向从8259A发送ICW2
	out  0xa1, al
	call io_delay

	;04h 分解成ICW3 相当于ICW[2] = 1, 
	;这表示从8259A通过主IRQ2管线连接到主8259A控制器
	mov  al, 0x04		; 向主8259A发送ICW3 
	out  0x21, al
	call io_delay

	mov  al, 0x02		;向从8259A 发送 ICW3 
	out  0xa1, al
	call io_delay

	mov  al, 0x02
	out  0x21, al
	call io_delay

	out  0xa1, al
	call io_delay


	;还需要再向两个芯片分别发送一个字节,叫OCW(operation control word), 
	;一个OCW是一字节数据,	也就是8bit,每一bit设置作用是,当OCW[i] = 1 时,
	;屏蔽对应的IRQ(i)管线的信号,例如OCW[0]=1, 那么IRQ0管线的信号将不会被CPU接收,以此类推
	;
	mov  al, 11111001b	;CPU只接收主8259A, IRQ1,IRQ2管线发送的信号,其他管线发送信号一概忽略
	out  0x21, al		;IRQ1对应的是键盘产生的中断
	call io_delay

	mov  al, 11101111b	;IRQ4 允许鼠标中断
	out  0xa1, al		;鼠标是通过从8259A的IRQ4管线向CPU发送信号
	call io_delay

	ret

io_delay:
     nop
     nop
     nop
     nop
     ret


	;检测内存数据
	LABEL_MEM_BUF:	;存放内存块
		times	256	db	0
	LABEL_MEM_COUNT:	;内存块数量
		dd	0


    [SECTION .s32]
    [BITS 32]
SEG_CODE32:
	
    mov ax,SelectorStack
    mov ss,ax 
    mov esp,STACK_TOP

    mov ax,SelectorVRAM
    mov ds,ax 
   
    call init_main 

fin:    
    hlt
    jmp fin

;8259A中断控制器
LABEL_8259A:
	SpuriousHandler  equ LABEL_8259A - $$
	iretd

;键盘中断程序
LabelKeyboardHandler:
	KeyboardHandler equ LabelKeyboardHandler - $$
	push es
    push ds
    pushad
    mov  eax, esp
    push eax
	
	call int_keyboard

	pop  eax
    mov  esp, eax
    popad
    pop  ds
    pop  es
	iretd

;鼠标中断程序
LabelMouseHandler:
	MouseHandler equ LabelMouseHandler - $$
	push es
    push ds
    pushad
    mov  eax, esp
    push eax
	
	call int_mouse

	pop  eax
    mov  esp, eax
    popad
    pop  ds
    pop  es
	iretd


;获取内存块数量
mem_block_count:
	mov eax,dword [LABEL_MEM_COUNT]
	ret 

mem_block_buf:
	mov eax,LABEL_MEM_BUF
	ret 


    ;导入io操作函数模块
    %include "io.s"


    ;导入C语言编写的功能模块
    %include "os.s"    

 
    ;32位模式代码长度
    SegCodeLen  EQU $-SEG_CODE32

    [SECTION .gs]
    ALIGN 32 
    [BITS 32]

    LABEL_STACK:
        times 1024 db 0
    STACK_TOP   EQU $ - LABEL_STACK

6.kernel.h

//申明汇编的功能模块给C语言调用

//获取内存块数量
extern int mem_block_count();

//获取内存块信息保存地址
extern void* mem_block_buf();

7.io.s

;io 操作函数定义,给C语言调用
;根据C语言函数调用规则,eax 寄存器作为返回值,edx 在定义的汇编函数中没有保存到栈中
;

[SECTION .s32]
[BITS 32]



;实现hlt 功能
;void io_hlt(); 
io_hlt:
    hlt
    ret


;读取指定端口8位数据
;char io_in8(int port);
io_in8:
    mov edx,[esp+4]
    mov eax,0
    in al,dx
    ret 



;读取指定端口16位数据
;int io_in16(int port);
io_in16:
    mov edx,[esp+4]
    mov eax,0
    in ax,dx
    ret 


;读取指定端口32位数据
;int io_in32(int port);
_io_in32:
    mov edx,[esp+4]
    in eax,dx
    ret 


;指定端口写入8位数据
;void io_out8(int port,char value);
io_out8:
    mov edx,[esp+4]
    mov al,[esp+8]
    out dx,al
    ret 



;指定端口写入16位数据
;void io_out16(int port,int value);
io_out16:
    mov edx,[esp+4]
    mov ax,[esp+8]
    out dx,ax
    ret 



;指定端口写入32位数据
;void io_out32(int port,int value);
io_out32:
    mov edx,[esp+4]
    mov eax,[esp+8]
    out dx,eax
    ret


;关闭cpu可屏蔽中断
;void io_cli();
io_cli:
    cli 
    ret 
    

;打开cpu可屏蔽中断
;void io_sti();
io_sti:
    sti  
    ret


;获取程序状态寄存器值
;int io_readFlag();
io_readFlag:
    pushfd 
    pop eax 
    ret 


;设置程序状态寄存器值
;void io_writeFlag(int value);
io_writeFlag:
    mov edx,[esp+4]
    push eax 
    popfd
    ret 

8.io.h

//
//  io.h
//  os-test
//
//  Created by vincent on 2018/10/31.
//  Copyright © 2018年 vincent. All rights reserved.
//




//实现hlt 功能
extern void io_hlt();


//读取指定端口8位数据
extern char io_in8(int port);


//读取指定端口16位数据
extern int io_in16(int port);


//读取指定端口32位数据
extern int io_in32(int port);


//指定端口写入8位数据
extern void io_out8(int port,char value);


//指定端口写入16位数据
extern void io_out16(int port,int value);


//指定端口写入32位数据
extern void io_out32(int port,int value);


//关闭cpu可屏蔽中断
extern void io_cli();


//打开cpu可屏蔽中断--在设置调色板时调用开启可屏蔽中断会奔溃
extern void io_sti();


//获取程序状态寄存器值
extern int io_readFlag();


//设置程序状态寄存器值
extern void io_writeFlag(int value);

9.ascii_font.h

//ascii 8x16点阵编码。每一行一个字节,共16行,为1表示显示
//每个字符的显示需要16个字节
//编码的都是可显示字符从ascii编码0x20-0x7f
//

static unsigned char ascii_array[] = {
				
		// space 0x20
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
		// !
		0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00,
		// "
		0x00,0x63,0x63,0x63,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
		// #
		0x00,0x00,0x00,0x36,0x36,0x7F,0x36,0x36,0x36,0x7F,0x36,0x36,0x00,0x00,0x00,0x00, 
		// $
		0x0C,0x0C,0x3E,0x63,0x61,0x60,0x3E,0x03,0x03,0x43,0x63,0x3E,0x0C,0x0C,0x00,0x00, 
		// %
		0x00,0x00,0x00,0x00,0x00,0x61,0x63,0x06,0x0C,0x18,0x33,0x63,0x00,0x00,0x00,0x00,
		// & 
		0x00,0x00,0x00,0x1C,0x36,0x36,0x1C,0x3B,0x6E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00, 
		// '
		0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
		// (
		0x00,0x00,0x0C,0x18,0x18,0x30,0x30,0x30,0x30,0x18,0x18,0x0C,0x00,0x00,0x00,0x00, 
		// )
		0x00,0x00,0x18,0x0C,0x0C,0x06,0x06,0x06,0x06,0x0C,0x0C,0x18,0x00,0x00,0x00,0x00, 
		// *
		0x00,0x00,0x00,0x00,0x42,0x66,0x3C,0xFF,0x3C,0x66,0x42,0x00,0x00,0x00,0x00,0x00,
		// +
		0x00,0x00,0x00,0x00,0x18,0x18,0x18,0xFF,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00, 
		// ,
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00, 
		// -
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
		// .
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00, 
		// / (forward slash)
		0x00,0x00,0x01,0x03,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00, 
		// 0 0x30
		0x00,0x00,0x3E,0x63,0x63,0x63,0x6B,0x6B,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00,
		// 1
		0x00,0x00,0x0C,0x1C,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3F,0x00,0x00,0x00,0x00, 
		// 2
		0x00,0x00,0x3E,0x63,0x03,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00, 
		// 3
		0x00,0x00,0x3E,0x63,0x03,0x03,0x1E,0x03,0x03,0x03,0x63,0x3E,0x00,0x00,0x00,0x00, 
		// 4
		0x00,0x00,0x06,0x0E,0x1E,0x36,0x66,0x66,0x7F,0x06,0x06,0x0F,0x00,0x00,0x00,0x00, 
		// 5
		0x00,0x00,0x7F,0x60,0x60,0x60,0x7E,0x03,0x03,0x63,0x73,0x3E,0x00,0x00,0x00,0x00, 
		// 6
		0x00,0x00,0x1C,0x30,0x60,0x60,0x7E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00, 
		// 7
		0x00,0x00,0x7F,0x63,0x03,0x06,0x06,0x0C,0x0C,0x18,0x18,0x18,0x00,0x00,0x00,0x00, 
		// 8
		0x00,0x00,0x3E,0x63,0x63,0x63,0x3E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00, 
		// 9
		0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x3F,0x03,0x03,0x06,0x3C,0x00,0x00,0x00,0x00, 
		// :
		0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00, 
		 // ;
		0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,
		// <
		0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00,
		// =
		0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00, 
		// >
		0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00, 
		// ?
		0x00,0x00,0x3E,0x63,0x63,0x06,0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x00,0x00,0x00,0x00, 
		// @ 0x40
		0x00,0x00,0x3E,0x63,0x63,0x6F,0x6B,0x6B,0x6E,0x60,0x60,0x3E,0x00,0x00,0x00,0x00, 
		// A
		0x00,0x00,0x08,0x1C,0x36,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x00,0x00,0x00,0x00,
		// B
		0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x33,0x33,0x33,0x33,0x7E,0x00,0x00,0x00,0x00, 
		// C
		0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x60,0x60,0x61,0x33,0x1E,0x00,0x00,0x00,0x00,
		// D
		0x00,0x00,0x7C,0x36,0x33,0x33,0x33,0x33,0x33,0x33,0x36,0x7C,0x00,0x00,0x00,0x00, 
		// E
		0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00, 
		// F
		0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00, 
		// G
		0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x6F,0x63,0x63,0x37,0x1D,0x00,0x00,0x00,0x00, 
		// H
		0x00,0x00,0x63,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00, 
		// I
		0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, 
		// J
		0x00,0x00,0x0F,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00,0x00,0x00, 
		// K
		0x00,0x00,0x73,0x33,0x36,0x36,0x3C,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00, 
		// L
		0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00, 
		// M
		0x00,0x00,0x63,0x77,0x7F,0x6B,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00, 
		// N
		0x00,0x00,0x63,0x63,0x73,0x7B,0x7F,0x6F,0x67,0x63,0x63,0x63,0x00,0x00,0x00,0x00, 
		// O
		0x00,0x00,0x1C,0x36,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x00,0x00,0x00,0x00, 
		 // P 0x50
		0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00,
		// Q
		0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x63,0x6B,0x6F,0x3E,0x06,0x07,0x00,0x00, 
		// R
		0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00, 
		// S
		0x00,0x00,0x3E,0x63,0x63,0x30,0x1C,0x06,0x03,0x63,0x63,0x3E,0x00,0x00,0x00,0x00, 
		// T
		0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, 
		// U
		0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00, 
		// V
		0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x08,0x00,0x00,0x00,0x00, 
		// W
		0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x36,0x00,0x00,0x00,0x00, 
		// X
		0x00,0x00,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x3C,0x66,0xC3,0xC3,0x00,0x00,0x00,0x00, 
		// Y
		0x00,0x00,0xC3,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, 
		// Z
		0x00,0x00,0x7F,0x63,0x43,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00, 
		// [
		0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00, 
		// \ (back slash)
		0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x07,0x03,0x01,0x00,0x00,0x00,0x00, 
		// ]
		0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00, 
		// ^
		0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
		// _
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00, 
		// ` 0x60
		0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
		// a
		0x00,0x00,0x00,0x00,0x00,0x3C,0x46,0x06,0x3E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00, 
		// b
		0x00,0x00,0x70,0x30,0x30,0x3C,0x36,0x33,0x33,0x33,0x33,0x6E,0x00,0x00,0x00,0x00, 
		// c
		0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x60,0x60,0x60,0x63,0x3E,0x00,0x00,0x00,0x00, 
		// d
		0x00,0x00,0x0E,0x06,0x06,0x1E,0x36,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00, 
		// e
		0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x7E,0x60,0x63,0x3E,0x00,0x00,0x00,0x00, 
		// f
		0x00,0x00,0x1C,0x36,0x32,0x30,0x7C,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00, 
		// g
		0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x66,0x3C,0x00,0x00, 
		// h
		0x00,0x00,0x70,0x30,0x30,0x36,0x3B,0x33,0x33,0x33,0x33,0x73,0x00,0x00,0x00,0x00, 
		// i
		0x00,0x00,0x0C,0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00, 
		// j
		0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00, 
		// k
		0x00,0x00,0x70,0x30,0x30,0x33,0x33,0x36,0x3C,0x36,0x33,0x73,0x00,0x00,0x00,0x00, 
		// l
		0x00,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00, 
		// m
		0x00,0x00,0x00,0x00,0x00,0x6E,0x7F,0x6B,0x6B,0x6B,0x6B,0x6B,0x00,0x00,0x00,0x00, 
		// n
		0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00, 
		// o
		0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00, 
		// p 0x70
		0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x3E,0x30,0x30,0x78,0x00,0x00, 
		// q
		0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x06,0x0F,0x00,0x00, 
		// r
		0x00,0x00,0x00,0x00,0x00,0x6E,0x3B,0x33,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00, 
		// s
		0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x38,0x0E,0x03,0x63,0x3E,0x00,0x00,0x00,0x00, 
		// t
		0x00,0x00,0x08,0x18,0x18,0x7E,0x18,0x18,0x18,0x18,0x1B,0x0E,0x00,0x00,0x00,0x00, 
		// u
		0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00, 
		// v
		0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x36,0x36,0x1C,0x1C,0x08,0x00,0x00,0x00,0x00, 
		// w
		0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x00,0x00,0x00,0x00, 
		// x
		0x00,0x00,0x00,0x00,0x00,0x63,0x36,0x1C,0x1C,0x1C,0x36,0x63,0x00,0x00,0x00,0x00, 
		// y
		0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x3F,0x03,0x06,0x3C,0x00,0x00, 
		// z
		0x00,0x00,0x00,0x00,0x00,0x7F,0x66,0x0C,0x18,0x30,0x63,0x7F,0x00,0x00,0x00,0x00, 
		// {
		0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00, 
		// |
		0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00, 
		// }
		0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00,
		// ~
		0x00,0x00,0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
		// DEL
		0x00,0x70,0xD8,0xD8,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

};

10.os.h

//*******************************相关数据类型声明*************************

//定义调色板颜色
#define  COL8_000000  0
#define  COL8_FF0000  1
#define  COL8_00FF00  2
#define  COL8_FFFF00  3
#define  COL8_0000FF  4
#define  COL8_FF00FF  5
#define  COL8_00FFFF  6
#define  COL8_FFFFFF  7
#define  COL8_C6C6C6  8
#define  COL8_840000  9
#define  COL8_008400  10
#define  COL8_848400  11
#define  COL8_000084  12
#define  COL8_840084  13
#define  COL8_008484  14
#define  COL8_848484  15


#define true 1
#define false 0

typedef struct _VRAM{
	//彩色屏幕显存地址
	unsigned char *addr;
	//平面宽度、高度
	int	screenW,screenH;
}VRAM;


//定义缓冲区
typedef struct _FIFO8{
	//指向缓冲区
    char* buf;
	//r:读索引,w:写索引
	//len:存储数据长度
    int r, w, size, len, flag;
}FIFO8;

//定义鼠标移动处理模型
//鼠标处理需要连续处理3字节
//phase 表三处理的字节阶段
//offX,offY 表示当前鼠标的偏移
//x,y 表示鼠标当前所在的坐标位置
typedef struct _MouseDes{
    char buf[3], phase;
	int offX,offY;
    int x, y, btn;
}MouseDes;

//*******************************函数声明*************************

//初始化调色板
void initPallet();



/**
 *绘制矩形
 *x             矩形左上角x坐标
 *y             矩形左上角y坐标
 *width         宽度
 *height        高度
 *colIndex      pallet_color 类型调色板颜色索引,即矩形颜色
 */

void fillRect(int x,int y,int width,int height,char colIndex);


//绘制桌面背景
void drawBackground();


/**
 *绘制字体
 *@param	addr		绘制的起始显存地址
 *@param 	x			绘制的x坐标
 *@param	y			绘制的y坐标
 *@param	col			绘制颜色
 *@param	pch			绘制的字符数组8*16,每一行共8位,共16行
 *@param	screenWidth	屏幕宽度
 */
void putChar(unsigned char *addr,int x,int y,char col,unsigned char *ch,int screenWidth);


/*
 *初始化鼠标指针
 *@param	vram		绘制的起始显存地址
 *@param	x			绘制鼠标指针最左上角x坐标
 *@param	y			绘制鼠标指针最左上角y坐标
 *@param	bc			绘制的矩形填充颜色,和背景色一样将能看到鼠标指针
 */
void drawMouseCursor(unsigned char *vram,int x,int y,char bc);



/*
 *char 类型数据转换为16进制字符数据
 *@param	val		待转化为16进制的数值
 *@return	返回的16进制字符数据(8位的整形数据变为16位的字符形似),获取时截取前两位即可
 */
short char2HexStr(unsigned char val);


//初始化鼠标中断硬件
void init_mouse_int();

//缓存初始化
void fifo8_init(FIFO8 *fifo,int size,char *buf);
//缓冲区存放数据
int fifo8_put(FIFO8 *fifo,char data);
//缓冲区读取数据
int fifo8_get(FIFO8 *fifo);

//鼠标移动处理
//@param	mdec		鼠标移动处理模型
//@param	bc			绘制鼠标的背景色
void mouseCursorMoved(MouseDes *mdec,char bc);


//如果是字符输出该字符,否则作为数据转换为16进制字符后输出
//buf	数据缓冲区
//len	字节数据长度
//isAscii	是否是ascii字符输出
void showString(unsigned char *buf,int len,char isAscii);

11.os.c

// !compile method
// clang -m32 -c os.c -o os.o
// objconv -fnasm os.o -o os.s
//

#include "os.h"
#include "io.h"
#include "kernel.h"
#include "ascii_font.h"

//************定义的全局变量前面加下划线防止和局部变量重名**************
//显存结构
static VRAM _vram;
//字体样式
static unsigned char *_ascii = ascii_array;
//数据临时内存区
static unsigned char _tempArr[64];
//键盘和鼠标缓存
static char _keybuf[32];
static char _mousebuf[128];

//键盘、鼠标中断信息
static FIFO8 _keybufInfo;
static FIFO8 _mousebufInfo;

//鼠标移动模型
static MouseDes _mouseDes;

static int _charCount = 0;



//操作系统C语言入口函数--可以指定为其他
void init_main() {
	_vram.addr = (unsigned char *)0xa0000;
	_vram.screenW = 320;
	_vram.screenH = 200;


	io_sti();
    initPallet();
	drawBackground();

	_tempArr[0] = '0';
	_tempArr[1] = 'x';
	

	int memCount = mem_block_count();
	short temp = char2HexStr(memCount);
	_tempArr[2] = ((char *)&temp)[0];
	_tempArr[3] = ((char *)&temp)[1];
	showString(_tempArr,4,true);

	fifo8_init(&_keybufInfo,32,_keybuf);
	fifo8_init(&_mousebufInfo,128,_mousebuf);

	_mouseDes.x = (320-16)/2;
	_mouseDes.y = (200-16)/2;
	_mouseDes.phase = 0;

	drawMouseCursor(_vram.addr,_mouseDes.x,_mouseDes.y,COL8_008484);
	
	init_mouse_int();
	
    for(; ;){
		if(_keybufInfo.len>0){
			io_cli();
			int len = _keybufInfo.len;
			for(int t=0;t<len;t++){
				char data = fifo8_get(&_keybufInfo);
				short temp = char2HexStr(data);
				_tempArr[2] = ((char *)&temp)[0];
				_tempArr[3] = ((char *)&temp)[1];
				showString(_tempArr,4,true);
			}

			io_sti();
		}
		else if(_mousebufInfo.len>0){
			io_cli();
			int len = _mousebufInfo.len;
			for(int t=0;t<len;t++){
				mouseCursorMoved(&_mouseDes,COL8_008484);
			}

			io_sti();
		}
		else{
       		io_hlt();
		}
		
    }
    
}

void initPallet(){
    //定义调色板
    static char table_rgb[16*3] = {
    
        0x00,  0x00,  0x00,		/*  0:黑色*/
        0xff,  0x00,  0x00,		/*  1:亮红*/
        0x00,  0xff,  0x00,		/*  2:亮绿*/
        0xff,  0xff,  0x00,		/*  3:亮黄*/
        0x00,  0x00,  0xff,		/*  4:亮蓝*/
        0xff,  0x00,  0xff,		/*  5:亮紫*/
        0x00,  0xff,  0xff,		/*  6:浅亮蓝*/
        0xff,  0xff,  0xff,		/*  7:白色*/
        0xc6,  0xc6,  0xc6,		/*  8:亮灰*/
        0x84,  0x00,  0x00,		/*  9:暗红*/
        0x00,  0x84,  0x00,		/* 10:暗绿*/
        0x84,  0x84,  0x00,		/* 11:暗黄*/
        0x00,  0x00,  0x84,		/* 12:暗青*/
        0x84,  0x00,  0x84,		/* 13:暗紫*/
        0x00,  0x84,  0x84,		/* 14:浅灰蓝*/
        0x84,  0x84,  0x84,		/* 15:暗灰*/
        
    };
    
	unsigned char *rgb = (unsigned char *)table_rgb;
	int flag = io_readFlag();
    io_cli();
    io_out8(0x03c8, 0);
	for(int i=0;i<16;i++){
		io_out8(0x03c9,rgb[0] / 4);
        io_out8(0x03c9,rgb[1] / 4);
        io_out8(0x03c9,rgb[2] / 4);
    	rgb += 3;
	}
	io_writeFlag(flag);
}


void fillRect(int x,int y,int width,int height,char colIndex){
    char *vram = (char *)_vram.addr;
    for(int i=y;i<=y+height;i++){
        for(int j=x;j<=x+width;j++){
            vram[i*_vram.screenW+j] = colIndex;
        }
    }
}


void drawBackground(){
	fillRect(0,0,_vram.screenW-1,_vram.screenH-29, COL8_008484);
    fillRect(0,_vram.screenH-28,_vram.screenW-1,28, COL8_848484);


	fillRect(0,_vram.screenH-27,_vram.screenW,1, COL8_848484);
	fillRect(0,_vram.screenH-26,_vram.screenW,25, COL8_C6C6C6);
	
	fillRect(3,_vram.screenH-24,56,1, COL8_FFFFFF);
	fillRect(2,_vram.screenH-24,1,20, COL8_FFFFFF);

	fillRect(3,_vram.screenH-4,56,1, COL8_848484);
	fillRect(59,_vram.screenH-23,1,19, COL8_848484);

	fillRect(2,_vram.screenH-3,57,0, COL8_000000);
	fillRect(60,_vram.screenH-24,0,19, COL8_000000);

	fillRect(_vram.screenW-47,_vram.screenH-24,43,1, COL8_848484);
	fillRect(_vram.screenW-47,_vram.screenH-23,0,19, COL8_848484);

	fillRect(_vram.screenW-47,_vram.screenH-3,43,0, COL8_FFFFFF);
	fillRect(_vram.screenW-3,_vram.screenH-24,0,21, COL8_FFFFFF);
}


void putChar(unsigned char *addr,int x,int y,char col,unsigned char *pch,int screenWidth){
	
	for(int i=0;i<16;i++){
		unsigned char ch = pch[i];
		int off = (y+i)*screenWidth;
		
		//显示的字形最左边的是低地址,右侧的是高地址。例如:0x80,则高地址部分显示在内存的低地址,
		//最低位的应该偏移7
		if((ch & 0x01) != 0){
			addr[off+x+7] = col;
		}
		if((ch & 0x02) != 0){
			addr[off+x+6] = col;
		}
		if((ch & 0x04) != 0){
			addr[off+x+5] = col;
		}
		if((ch & 0x08) != 0){
			addr[off+x+4] = col;
		}	
		if((ch & 0x10) != 0){
			addr[off+x+3] = col;
		}
		if((ch & 0x20) != 0){
			addr[off+x+2] = col;
		}
		if((ch & 0x40) != 0){
			addr[off+x+1] = col;
		}	
		if((ch & 0x80) != 0){
			addr[off+x+0] = col;
		}
	}
}


void drawMouseCursor(unsigned char *vram,int x,int y,char bc){
	//16*16 Mouse 
    //鼠标指针点阵
	static char cursor[16][16] = {
	 "*...............",
	 "**..............",
	 "*O*.............",
	 "*OO*............",
	 "*OOO*...........",
	 "*OOOO*..........",
	 "*OOOOO*.........",
	 "*OOOOOO*........",
	 "*OOOOOOO*.......",
	 "*OOOO*****......",
	 "*OO*O*..........",
	 "*O*.*O*.........",
	 "**..*O*.........",
	 "*....*O*........",
	 ".....*O*........",
	 "......*........."
	};
	
	for (int i = 0; i < 16; i++) {
		for (int j = 0; j < 16; j++) {
			int off = (i+y)*_vram.screenW+x+j;
			if (cursor[i][j] == '*') {
				vram[off] = COL8_000000;
			}
			if (cursor[i][j] == 'O') {
				vram[off] = COL8_FFFFFF;
			}
			if (cursor[i][j] == '.') {
				vram[off] = bc;
			}
		}
	}

}


short char2HexStr(unsigned char val) {
	short tmp = val >> 4;
	short rst = 0;
	if(tmp>=10){
		((char*)&rst)[0] = 'a'+tmp-10;
	}
	else{
		((char*)&rst)[0] = '0'+tmp;
	}

	tmp = val & 0x0f;
	if(tmp>=10){
		((char*)&rst)[1] = 'a'+tmp-10;
	}
	else{
		((char*)&rst)[1] = '0'+tmp;
	}

	return rst;
}


/*
 *8259A 键盘中断调用
 *
 */
void int_keyboard(char *index){
	//0x20是8259A控制端口
	//0x21对应的是键盘的中断向量。当键盘中断被CPU执行后,下次键盘再向CPU发送信号时,
	//CPU就不会接收,要想让CPU再次接收信号,必须向主PIC的端口再次发送键盘中断的中断向量号
	io_out8(0x20,0x21);

	//读取8259A  0x60端口键盘扫描码
	char data = io_in8(0x60);

	fifo8_put(&_keybufInfo,data);
}


#define  PORT_KEYDAT  0x60
#define  PORT_KEYSTA  0x64
#define  PORT_KEYCMD  0x64
#define  KEYCMD_WRITE_MODE  0x60
#define  KBC_MODE     0x47

//鼠标电路对应的一个端口是 0x64, 通过读取这个端口的数据来检测鼠标电路的状态,
//内核会从这个端口读入一个字节的数据,如果该字节的第二个比特位为0,那表明鼠标电路可以
//接受来自内核的命令,因此,在给鼠标电路发送数据前,内核需要反复从0x64端口读取数据,
//并检测读到数据的第二个比特位,直到该比特位为0时,才发送控制信息
void waitKBCReady(){
	for( ; ;){
		if((io_in8(PORT_KEYSTA) & 0x02)==0){
			break;
		}
	}
}


#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4

//初始化键盘控制电路,鼠标控制电路是连接在键盘控制电路上,通过键盘电路实现初始化
void init_mouse_int(){
	waitKBCReady();

	//0x60让键盘电路进入数据接受状态
	io_out8(PORT_KEYCMD,KEYCMD_WRITE_MODE);
	waitKBCReady();

	//数据0x47要求键盘电路启动鼠标模式,这样鼠标硬件所产生的数据信息,通过键盘电路端口0x60就可读到
	io_out8(PORT_KEYDAT,KBC_MODE);	
	
	waitKBCReady();
	io_out8(PORT_KEYCMD,KEYCMD_SENDTO_MOUSE);
	waitKBCReady();
	//0xf4数据激活鼠标电路,激活后将会给CPU发送中断信号
	io_out8(PORT_KEYDAT,MOUSECMD_ENABLE);
}



/*
 *8259A 鼠标中断调用
 *
 */
void int_mouse(char *index){
	//当中断处理后,要想再次接收中断信号,就必须向中断控制器发送一个字节的数据
	io_out8(0x20,0x20);
	io_out8(0xa0,0x20);
	
	//读取鼠标数据
	char data = io_in8(0x60);
	fifo8_put(&_mousebufInfo,data);
}


void fifo8_init(FIFO8 *fifo, int size,char *buf){
	fifo->buf = buf;
	fifo->r = 0;
	fifo->w = 0;
	fifo->size = size;
	fifo->len = 0;
	fifo->flag = 0;
}


int fifo8_put(FIFO8 *fifo,char data){
    if (fifo->len == fifo->size) {
        return -1;
    }

    fifo->buf[fifo->w] = data;
    fifo->w++;
    if (fifo->w == fifo->size) {
        fifo->w = 0;
    }

    fifo->len++;
    return 0;
}


int fifo8_get(FIFO8 *fifo) {
    
    if (fifo->len == 0) {
        return -1;
    }

   	int data = fifo->buf[fifo->r];
    fifo->r++;
    if (fifo->r == fifo->size) {
        fifo->r = 0;
    }

    fifo->len--;
    return data;
}


int mouse_decode(MouseDes *mdec,unsigned char dat){ 
	int flag = -1;
	if (mdec->phase == 0) { 
		if (dat == 0xfa) { 
			mdec->phase = 1; 
		} 
		flag = 0;
	} 
	else if (mdec->phase == 1) { 
		if ((dat & 0xc8) == 0x08) { 
			mdec->buf[0] = dat; 
			mdec->phase = 2; 
		} 
		flag = 0;
	} 
	else if (mdec->phase == 2) { 
		mdec->buf[1] = dat; 
		mdec->phase = 3; 
		flag = 0;
	} 
	else if (mdec->phase == 3) { 
		mdec->buf[2] = dat; 
		mdec->phase = 1; 
		mdec->btn = mdec->buf[0] & 0x07; 
		mdec->offX = mdec->buf[1]; 
		mdec->offY = mdec->buf[2]; 
		if ((mdec->buf[0] & 0x10) != 0) { 
			mdec->offX |= 0xffffff00; 
		} 
		if ((mdec->buf[0] & 0x20) != 0) { 
			mdec->offY |= 0xffffff00; 
		} 
		//鼠标y坐标偏移和平面y坐标相反
		mdec->offY = -mdec->offY; 
		flag = 1; 
	} 
	return flag; 
}

//鼠标移动处理
void mouseCursorMoved(MouseDes *mdec,char bc){
	unsigned char val = fifo8_get(&_mousebufInfo);
		
	//表示处理到第3步,需要绘制鼠标光标
	if(mouse_decode(mdec,val) == 1) {
		fillRect(mdec->x,mdec->y,16,16,bc);

		mdec->x += mdec->offX;
		mdec->y += mdec->offY;
		if(mdec->x < 0){
			mdec->x = 0;
		}
		if(mdec->x > _vram.screenW-16/2){
			mdec->x = _vram.screenW-16/2;
		}

		if(mdec->y < 0 ){
			mdec->y = 0;
		}
		if(mdec->y > _vram.screenH - 16){
			mdec->y = _vram.screenH - 16;
		}
		drawMouseCursor(_vram.addr,mdec->x,mdec->y,bc);
	}
}


void showString(unsigned char *buf,int len,char isAscii){
	for(int i=0;i<len;i++){
		int ch = buf[i];
		if(isAscii){
			int x = (_charCount)%32*10;
			int y = (_charCount)/32*20;
			putChar(_vram.addr,x,y,COL8_FFFFFF,_ascii+(ch-0x20)*16,_vram.screenW);
			_charCount++;
		
		}
		else{
			ch = char2HexStr(buf[i]);
			for(int j=0;j<2;j++){
				int x = (_charCount)%32*10;
				int y = (_charCount)/32*20;

				putChar(_vram.addr,x,y,COL8_FFFFFF,_ascii+(((unsigned char *)&ch)[j]-0x20)*16,_vram.screenW);
				
				_charCount++;
			}
		}
	}
}

注意

开发操作系统过程中在引入C语言后特别需要注意数据类型的问题,笔者使用习惯高级开发语言,很少使用数据类型字节数比较小的char,大多都是int 型,导致在开发过程中是 unsigned char 类型的喜欢用char,这样导致的一个后果就是与预期不符,思来想去花费了大量时间才排查出问题。开发中很少设计有符号数运算,基本都是无符号类型,建议大家在遇到字节型数据类型时多留意是否是有符号数。

一、开发平台:Linux
图形界面的Ubuntu环境是一个非常不错的选择,安装各种工具都非常方便。

二、开发工具
1.需要使用苹果编译器clang编译C语言,非常容易安装(sudo apt-get install -y clang);

2.反汇编器objconv,文章末尾有相关下载说明,运行相关命令生成objconv 反汇编器后不配置环境变量需要将objconv 复制到操作系统相关文件夹中(boot.s所在文件夹)。在ubuntu-18.04.1 中笔者编译objconv 时就失败,应该是版本相对较高或者相关编译环境的问题,但是这个完全不影响对操作系统的开发学习。后来在低版本Ubuntu环境中编译了objconv,为学者减少不必要的工具困难,笔者把64位Ubuntu下的objconv 也上传,对我们的操作系统开发的objconv 也在下载的代码中,编译都需要进入该文件夹中;

3.汇编器nasm也是非常容易安装(sudo apt-get install -y nasm);

4.虚拟机VirtualBox加载运行生成的虚拟软盘文件floppy.img,下载链接: https://pan.baidu.com/s/1MN2-tY8lduQRRcLvbyJT-Q 提取码: w3ca

5.Ubuntu自带的gedit图形化文本编辑器非常不错,但是对于2进制就无法打开,对于操作系统开发我们希望能了解更多!Sublime就是一个非常不错的工具,对于无法以文本显示就会以16进制显示。虽然是商业软件,没有购买会有弹框弹出,但是关闭即可且没有使用期限限制,真心不错
安装如下:

sudo add-apt-repository ppa:webupd8team/sublime-text-2
sudo apt-get update
sudo apt-get install sublime-text

三、编译方式
1.clang生成软盘制作工具main:clang main.c floppy.c -o main

2.clang 编译器编译os.c为二进制:clang -c -m32 os.c -o os.o

3.objconv 反汇编os.o 为汇编文件os.s:./objconv -fnasm os.o -o os.s,并去除os.s 中的global、extern、SECTION 等无用声明

4.编译内核加载器boot.s为二进制boot.bat:nasm boot.s -o boot.bat

5.编译kernel.s为二进制kernel.bat:nasm kernel.s -o kernel.bat

6.运行软盘制作工具生成虚拟软盘floppy.img: ./main

四、运行方式
先创建虚拟软盘控制器再加载运行虚拟软盘floppy.img

代码下载:链接:https://pan.baidu.com/s/1c9RTHu9Q3rYEoZfFlHmVkg 提取码: q9u2

开发工具链接: https://pan.baidu.com/s/1_rHVpqeuSeDV3YreEPXU1w 提取码: 2unk

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值