美文网首页
【中断开发:实现时钟中断】(草稿)

【中断开发:实现时钟中断】(草稿)

作者: 月下蓑衣江湖夜雨 | 来源:发表于2020-11-26 23:06 被阅读0次

const.h

#ifndef _CONST_H_
#define _CONST_H_


/* 函数类型 */
#define PUBLIC          /* PUBLIC is the opposite of PRIVATE,宏展开了啥都没有,其实没有啥作用,只是提示一下阅读,该函数,类外可见 */
#define PRIVATE static  /* static源文件可见 */

/* GDT 和 IDT 中描述符的个数 */
#define GDT_SIZE    128
#define IDT_SIZE    256

/* Boolean */
#define TRUE    1
#define FALSE   0

/* GDT 和 IDT 中描述符的个数 */
#define GDT_SIZE    128
#define IDT_SIZE    256

/* 权限 */
#define PRIVILEGE_KRNL  0
#define PRIVILEGE_TASK  1
#define PRIVILEGE_USER  3

/* 8259A interrupt controller ports. */
#define INT_M_CTL   0x20    /* I/O port for interrupt controller         <Master> */
#define INT_M_CTLMASK   0x21    /* setting bits in this port disables ints   <Master> */
#define INT_S_CTL   0xA0    /* I/O port for second interrupt controller  <Slave>  */
#define INT_S_CTLMASK   0xA1    /* setting bits in this port disables ints   <Slave>  */

#endif

kernel.asm

SELECTOR_KERNEL_CS  equ 8

; 导入函数
extern  cstart
extern  exception_handler
extern  spurious_irq

; 导入全局变量
extern  gdt_ptr
extern  idt_ptr

[SECTION .bss]
StackSpace      resb    2 * 1024
StackTop:       ; 栈顶

[section .text] ; 代码在此

global _start   ; 导出 _start

global  divide_error
global  single_step_exception
global  nmi
global  breakpoint_exception
global  overflow
global  bounds_check
global  inval_opcode
global  copr_not_available
global  double_fault
global  copr_seg_overrun
global  inval_tss
global  segment_not_present
global  stack_exception
global  general_protection
global  page_fault
global  copr_error
global  hwint00
global  hwint01
global  hwint02
global  hwint03
global  hwint04
global  hwint05
global  hwint06
global  hwint07
global  hwint08
global  hwint09
global  hwint10
global  hwint11
global  hwint12
global  hwint13
global  hwint14
global  hwint15

_start:
    ; 此时内存看上去是这样的(更详细的内存情况在 LOADER.ASM 中有说明):
    ;              ┃                                    ┃
    ;              ┃                 ...                ┃
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■Page  Tables■■■■■■┃
    ;              ┃■■■■■(大小由LOADER决定)■■■■┃ PageTblBase
    ;    00101000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■Page Directory Table■■■■┃ PageDirBase = 1M
    ;    00100000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃□□□□ Hardware  Reserved □□□□┃ B8000h ← gs
    ;       9FC00h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp
    ;       90000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■■KERNEL.BIN■■■■■■┃
    ;       80000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■■■KERNEL■■■■■■■┃ 30400h ← KERNEL 入口 (KernelEntryPointPhyAddr)
    ;       30000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┋                 ...                ┋
    ;              ┋                                    ┋
    ;           0h ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
    ;
    ;
    ; GDT 以及相应的描述符是这样的:
    ;
    ;                     Descriptors               Selectors
    ;              ┏━━━━━━━━━━━━━━━━━━┓
    ;              ┃         Dummy Descriptor           ┃
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃         DESC_FLAT_C    (0~4G)     ┃   8h = cs
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃         DESC_FLAT_RW   (0~4G)     ┃  10h = ds, es, fs, ss
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃         DESC_VIDEO                 ┃  1Bh = gs
    ;              ┗━━━━━━━━━━━━━━━━━━┛
    ;
    ; 注意! 在使用 C 代码的时候一定要保证 ds, es, ss 这几个段寄存器的值是一样的
    ; 因为编译器有可能编译出使用它们的代码, 而编译器默认它们是一样的. 比如串拷贝操作会用到 ds 和 es.
    ;
    ;


    ; 把 esp 从 LOADER 挪到 KERNEL
    mov esp, StackTop   ; 堆栈在 bss 段中
    sgdt    [gdt_ptr]   ; cstart() 中将会用到 gdt_ptr
    call    cstart      ; 在此函数中改变了gdt_ptr,让它指向新的GDT
    lgdt    [gdt_ptr]   ; 使用新的GDT
    lidt    [idt_ptr]   ; 使用IDT
    ud2
    jmp $


    
; 中断和异常 -- 硬件中断
; ---------------------------------
%macro  hwint_master    1
    push    %1
    call    spurious_irq
    add esp, 4
    hlt
%endmacro


ALIGN   16
hwint00:        ; Interrupt routine for irq 0 (the clock).
    hwint_master    0

ALIGN   16
hwint01:        ; Interrupt routine for irq 1 (keyboard)
    hwint_master    1

ALIGN   16
hwint02:        ; Interrupt routine for irq 2 (cascade!)
    hwint_master    2

ALIGN   16
hwint03:        ; Interrupt routine for irq 3 (second serial)
    hwint_master    3

ALIGN   16
hwint04:        ; Interrupt routine for irq 4 (first serial)
    hwint_master    4

ALIGN   16
hwint05:        ; Interrupt routine for irq 5 (XT winchester)
    hwint_master    5

ALIGN   16
hwint06:        ; Interrupt routine for irq 6 (floppy)
    hwint_master    6

ALIGN   16
hwint07:        ; Interrupt routine for irq 7 (printer)
    hwint_master    7

; ---------------------------------
%macro  hwint_slave 1
    push    %1
    call    spurious_irq
    add esp, 4
    hlt
%endmacro
; ---------------------------------

ALIGN   16
hwint08:        ; Interrupt routine for irq 8 (realtime clock).
    hwint_slave 8

ALIGN   16
hwint09:        ; Interrupt routine for irq 9 (irq 2 redirected)
    hwint_slave 9

ALIGN   16
hwint10:        ; Interrupt routine for irq 10
    hwint_slave 10

ALIGN   16
hwint11:        ; Interrupt routine for irq 11
    hwint_slave 11

ALIGN   16
hwint12:        ; Interrupt routine for irq 12
    hwint_slave 12

ALIGN   16
hwint13:        ; Interrupt routine for irq 13 (FPU exception)
    hwint_slave 13

ALIGN   16
hwint14:        ; Interrupt routine for irq 14 (AT winchester)
    hwint_slave 14

ALIGN   16
hwint15:        ; Interrupt routine for irq 15
    hwint_slave 15



; 中断和异常 -- 异常
divide_error:
    push    0xFFFFFFFF  ; no err code
    push    0       ; vector_no = 0
    jmp exception
single_step_exception:
    push    0xFFFFFFFF  ; no err code
    push    1       ; vector_no = 1
    jmp exception
nmi:
    push    0xFFFFFFFF  ; no err code
    push    2       ; vector_no = 2
    jmp exception
breakpoint_exception:
    push    0xFFFFFFFF  ; no err code
    push    3       ; vector_no = 3
    jmp exception
overflow:
    push    0xFFFFFFFF  ; no err code
    push    4       ; vector_no = 4
    jmp exception
bounds_check:
    push    0xFFFFFFFF  ; no err code
    push    5       ; vector_no = 5
    jmp exception
inval_opcode:
    push    0xFFFFFFFF  ; no err code
    push    6       ; vector_no = 6
    jmp exception
copr_not_available:
    push    0xFFFFFFFF  ; no err code
    push    7       ; vector_no = 7
    jmp exception
double_fault:
    push    8       ; vector_no = 8
    jmp exception
copr_seg_overrun:
    push    0xFFFFFFFF  ; no err code
    push    9       ; vector_no = 9
    jmp exception
inval_tss:
    push    10      ; vector_no = A
    jmp exception
segment_not_present:
    push    11      ; vector_no = B
    jmp exception
stack_exception:
    push    12      ; vector_no = C
    jmp exception
general_protection:
    push    13      ; vector_no = D
    jmp exception
page_fault:
    push    14      ; vector_no = E
    jmp exception
copr_error:
    push    0xFFFFFFFF  ; no err code
    push    16      ; vector_no = 10h
    jmp exception

exception:
    call    exception_handler
    add esp, 4*2    ; 让栈顶指向 EIP,堆栈中从顶向下依次是:EIP、CS、EFLAGS
    hlt 

klib.asm

[SECTION .data]
disp_pos            dd  0
disp_pos_2          dd  0

[SECTION .text]

; 导出函数
global  DspStr
global  DspStrFixPos
global  memcpy
global  out_byte
global  in_byte

; ========================================================================
;                  void DspStr(char * info);
; ========================================================================
DspStr:
    push    ebp
    mov ebp, esp

    mov esi, [ebp + 8]  ; pszInfo
    mov edi, [disp_pos]
    mov ah, 0Fh
.1:
    lodsb
    test    al, al
    jz  .2
    cmp al, 0Ah ; 是回车吗?
    jnz .3
    push    eax
    mov eax, edi
    mov bl, 160
    div bl
    and eax, 0FFh
    inc eax
    mov bl, 160
    mul bl
    mov edi, eax
    pop eax
    jmp .1
.3:
    mov [gs:edi], ax
    add edi, 2
    jmp .1

.2:
    mov [disp_pos], edi

    pop ebp
    ret
    
; ========================================================================
;                  void DspStrFixLn(int line, char * info);
; ========================================================================
DspStrFixPos:
    push    ebp
    mov ebp, esp
    
    mov ebx, [ebp + 8]  ; pszLine
    mov [disp_pos_2], ebx

    mov esi, [ebp + 12] ; pszInfo
    mov edi, ebx;[disp_pos_2]
    mov ah, 0Fh
.1:
    lodsb
    test    al, al
    jz  .2
    cmp al, 0Ah ; 是回车吗?
    jnz .3
    push    eax
    mov eax, edi
    mov bl, 160
    div bl
    and eax, 0FFh
    inc eax
    mov bl, 160
    mul bl
    mov edi, eax
    pop eax
    jmp .1
.3:
    mov [gs:edi], ax
    add edi, 2
    jmp .1

.2:
    ;mov    [disp_pos_2], edi

    pop ebp
    ret 

; 导出函数
; global    memcpy

; ========================================================================
;                  void disp_color_str(char * info, int color);
; ========================================================================
disp_color_str:
    push    ebp
    mov ebp, esp

    mov esi, [ebp + 8]  ; pszInfo
    mov edi, [disp_pos]
    mov ah, [ebp + 12]  ; color
.1:
    lodsb
    test    al, al
    jz  .2
    cmp al, 0Ah ; 是回车吗?
    jnz .3
    push    eax
    mov eax, edi
    mov bl, 160
    div bl
    and eax, 0FFh
    inc eax
    mov bl, 160
    mul bl
    mov edi, eax
    pop eax
    jmp .1
.3:
    mov [gs:edi], ax
    add edi, 2
    jmp .1

.2:
    mov [disp_pos], edi

    pop ebp
    ret

; ------------------------------------------------------------------------
; void* memcpy(void* es:pDest, void* ds:pSrc, int iSize);
; ------------------------------------------------------------------------
memcpy:
    push    ebp
    mov ebp, esp

    push    esi
    push    edi
    push    ecx

    mov edi, [ebp + 8]  ; Destination
    mov esi, [ebp + 12] ; Source
    mov ecx, [ebp + 16] ; Counter
.1:
    cmp ecx, 0      ; 判断计数器
    jz  .2      ; 计数器为零时跳出

    mov al, [ds:esi]        ; ┓
    inc esi         ; ┃
                    ; ┣ 逐字节移动
    mov byte [es:edi], al   ; ┃
    inc edi         ; ┛

    dec ecx     ; 计数器减一
    jmp .1      ; 循环
.2:
    mov eax, [ebp + 8]  ; 返回值

    pop ecx
    pop edi
    pop esi
    mov esp, ebp
    pop ebp

    ret         ; 函数结束,返回
; memcpy 结束-------------------------------------------------------------    

; ========================================================================
;                  void out_byte(t_port port, t_8 value);
; ========================================================================
out_byte:
    mov edx, [esp + 4]      ; port
    mov al, [esp + 4 + 4]   ; value
    out dx, al
    nop ; 一点延迟
    nop
    ret

; ========================================================================
;                  t_8 in_byte(t_port port);
; ========================================================================
in_byte:
    mov edx, [esp + 4]      ; port
    xor eax, eax
    in  al, dx
    nop ; 一点延迟
    nop
    ret

protect.h

#ifndef _PROTECT_H_
#define _PROTECT_H_

/* 存储段描述符/系统段描述符 */
typedef struct s_descriptor {   /* 共 8 个字节 */
    t_16    limit_low;          /* Limit */
    t_16    base_low;           /* Base */
    t_8 base_mid;               /* Base */
    t_8 attr1;                  /* P(1) DPL(2) DT(1) TYPE(4) */
    t_8 limit_high_attr2;       /* G(1) D(1) 0(1) AVL(1) LimitHigh(4) */
    t_8 base_high;              /* Base */
} DESCRIPTOR;

/* 门描述符 */
typedef struct s_gate {
    t_16    offset_low;     /* Offset Low */
    t_16    selector;       /* Selector */
    t_8     dcount;         /* 该字段只在调用门描述符中有效。
                               如果在利用调用门调用子程序时引起特权级的转换和堆栈的改变,需要将外层堆栈中的参数复制到内层堆栈。
                               该双字计数字段就是用于说明这种情况发生时,要复制的双字参数的数量。 */
    t_8     attr;           /* P(1) DPL(2) DT(1) TYPE(4) */
    t_16    offset_high;    /* Offset High */
} GATE;

/* 中断向量 */
#define INT_VECTOR_IRQ0         0x20
#define INT_VECTOR_IRQ8         0x28

#endif

start.c

#include "type.h"
#include "const.h"
#include "protect.h"

/* 选择子 */
#define SELECTOR_DUMMY         0        // ┓
#define SELECTOR_FLAT_C     0x08        // ┣ LOADER 里面已经确定了的.
#define SELECTOR_FLAT_RW    0x10        // ┃
#define SELECTOR_VIDEO      (0x18+3)    // ┛<-- RPL=3

#define DA_386IGate     0x8E    /* 386 中断门类型值           */
#define SELECTOR_KERNEL_CS  SELECTOR_FLAT_C
#define SELECTOR_KERNEL_DS  SELECTOR_FLAT_RW

PUBLIC  void*   memcpy(void* pDst, void* pSrc, int iSize);
PUBLIC  void    DspStr(char * pszInfo);
PUBLIC  void    DspStrFixPos(int line, char * pszInfo);
PUBLIC  int     sprintf(char *buf, const char *fmt, ...);
PRIVATE void    memcpy_gdt();

PUBLIC void init_idt();
PUBLIC void init_prot();
PUBLIC void init_8259A();
PUBLIC void spurious_irq(int irq);
PUBLIC void exception_handler(int vec_no, int err_code, int eip, int cs, int eflags);
PUBLIC char * itoa(char * str, int num);
//PUBLIC void disp_int(int input);

/* 本文件内函数声明 */
PRIVATE void init_idt_desc(unsigned char vector, t_8 desc_type, t_pf_int_handler handler, unsigned char privilege);
//extern    int disp_pos;


/* 中断处理函数 */
void    divide_error();
void    single_step_exception();
void    nmi();
void    breakpoint_exception();
void    overflow();
void    bounds_check();
void    inval_opcode();
void    copr_not_available();
void    double_fault();
void    copr_seg_overrun();
void    inval_tss();
void    segment_not_present();
void    stack_exception();
void    general_protection();
void    page_fault();
void    copr_error();
void    hwint00();
void    hwint01();
void    hwint02();
void    hwint03();
void    hwint04();
void    hwint05();
void    hwint06();
void    hwint07();
void    hwint08();
void    hwint09();
void    hwint10();
void    hwint11();
void    hwint12();
void    hwint13();
void    hwint14();
void    hwint15();

PUBLIC void init_prot();


// 中断
void out_byte(t_port port, t_8 value);
t_8  in_byte(t_port port);

PUBLIC  t_8             gdt_ptr[6];                   // 0~15:Limit  16~47:Base
PUBLIC  DESCRIPTOR      gdt[GDT_SIZE];
PUBLIC  t_8             idt_ptr[6];                   // 0~15:Limit  16~47:Base
PUBLIC  GATE            idt[IDT_SIZE];

char *str0 = "hello os world, last line";
char *str1 = "hello os world, line111\n";
static char buf[1024];                                // 显示用临时缓冲区

/*======================================================================*/
PUBLIC void cstart(){
    //DspStrFixPos((80*20+0)*2, str0);
    memcpy_gdt();
    
    
    for(int i=0; i<100; i++){
        sprintf(buf, "hello os printk year is %d, month is %d, day is %d i is %d\n", 2020, 11, 22, i);
        DspStrFixPos((80*24+0)*2, buf);
    }
    
    //sprintf(buf, "hello os printk year is %d, month is %d, day is %d\n", 2020, 11, 8);
    //DspStrFixPos(12, buf);
    //sprintf(buf, "hello os sprintf, time is %d:%d\n", 14, 38);
    //DspStrFixPos(13, buf);
}

PUBLIC void memcpy_gdt() {
    // 将 LOADER 中的 GDT 复制到新的 GDT 中
    memcpy( &gdt,                   // New GDT
        (void*)(*((t_32*)(&gdt_ptr[2]))),   // Base  of Old GDT
        *((t_16*)(&gdt_ptr[0])) + 1     // Limit of Old GDT
        );
    // gdt_ptr[6] 共 6 个字节:0~15:Limit  16~47:Base。用作 sgdt 以及 lgdt 的参数。
    t_16* p_gdt_limit = (t_16*)(&gdt_ptr[0]);
    t_32* p_gdt_base  = (t_32*)(&gdt_ptr[2]);
    *p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR) - 1;
    *p_gdt_base  = (t_32)&gdt;
}

PUBLIC void init_idt() {
    init_prot();
    // idt_ptr[6] 共 6 个字节:0~15:Limit  16~47:Base。用作 sidt 以及 lidt 的参数。
    t_16* p_idt_limit = (t_16*)(&idt_ptr[0]);
    t_32* p_idt_base  = (t_32*)(&idt_ptr[2]);
    *p_idt_limit = IDT_SIZE * sizeof(GATE) - 1;
    *p_idt_base  = (t_32)&idt;
}
/*======================================================================*
                            init_8259A
 *======================================================================*/
PUBLIC void init_8259A(){
    out_byte(INT_M_CTL, 0x11);                  // Master 8259, ICW1.
    out_byte(INT_S_CTL, 0x11);                  // Slave  8259, ICW1.
    out_byte(INT_M_CTLMASK, INT_VECTOR_IRQ0);   // Master 8259, ICW2. 设置 '主8259' 的中断入口地址为 0x20.
    out_byte(INT_S_CTLMASK, INT_VECTOR_IRQ8);   // Slave  8259, ICW2. 设置 '从8259' 的中断入口地址为 0x28
    out_byte(INT_M_CTLMASK, 0x4);               // Master 8259, ICW3. IR2 对应 '从8259'.
    out_byte(INT_S_CTLMASK, 0x2);               // Slave  8259, ICW3. 对应 '主8259' 的 IR2.
    out_byte(INT_M_CTLMASK, 0x1);               // Master 8259, ICW4.
    out_byte(INT_S_CTLMASK, 0x1);               // Slave  8259, ICW4.

    out_byte(INT_M_CTLMASK, 0xFD);              // Master 8259, OCW1. 
    out_byte(INT_S_CTLMASK, 0xFF);              // Slave  8259, OCW1. 
}

/*======================================================================*
                           spurious_irq
 *======================================================================*/
PUBLIC void spurious_irq(int irq){
    //disp_str("spurious_irq: ");
    //disp_int(irq);
    //disp_str("\n");
    sprintf(buf, "spurious_irq is %d\n", irq);
    DspStrFixPos((80*24+0)*2, buf);
}

/*======================================================================*
                            exception_handler
 *----------------------------------------------------------------------*
 异常处理
 *======================================================================*/
PUBLIC void exception_handler(int vec_no, int err_code, int eip, int cs, int eflags){
    int i;
    int text_color = 0x74; /* 灰底红字 */
    char err_description[][64] = {  "#DE Divide Error",
                    "#DB RESERVED",
                    "—  NMI Interrupt",
                    "#BP Breakpoint",
                    "#OF Overflow",
                    "#BR BOUND Range Exceeded",
                    "#UD Invalid Opcode (Undefined Opcode)",
                    "#NM Device Not Available (No Math Coprocessor)",
                    "#DF Double Fault",
                    "    Coprocessor Segment Overrun (reserved)",
                    "#TS Invalid TSS",
                    "#NP Segment Not Present",
                    "#SS Stack-Segment Fault",
                    "#GP General Protection",
                    "#PF Page Fault",
                    "—  (Intel reserved. Do not use.)",
                    "#MF x87 FPU Floating-Point Error (Math Fault)",
                    "#AC Alignment Check",
                    "#MC Machine Check",
                    "#XF SIMD Floating-Point Exception"
                };

    /* 通过打印空格的方式清空屏幕的前五行,并把 disp_pos 清零 */
    //disp_pos = 0;
    for(i=0;i<80*5;i++){
        DspStr(" ");
    }
    //disp_pos = 0;

    DspStr("Exception! --> ");
    DspStr(err_description[vec_no]);
    DspStr("\n\n");
    DspStr("EFLAGS:");
    //disp_int(eflags);
    sprintf(buf, "%d", eflags);
    DspStr(buf);
    DspStr("CS:");
    //disp_int(cs);
    sprintf(buf, "%d", cs);
    DspStr(buf);
    DspStr("EIP:");
    //disp_int(eip);
    sprintf(buf, "%d", eip);
    DspStr(buf);

    if(err_code != 0xFFFFFFFF){
        DspStr("Error code:");
        //disp_int(err_code);
        sprintf(buf, "%d", err_code);
        DspStr(buf);
    }
}

/*======================================================================*
                               itoa
 *======================================================================*/
PUBLIC char * itoa(char * str, int num)/* 数字前面的 0 不被显示出来, 比如 0000B800 被显示成 B800 */
{
    char *  p = str;
    char    ch;
    int i;
    t_bool  flag = FALSE;

    *p++ = '0';
    *p++ = 'x';

    if(num == 0){
        *p++ = '0';
    }
    else{   
        for(i=28;i>=0;i-=4){
            ch = (num >> i) & 0xF;
            if(flag || (ch > 0)){
                flag = TRUE;
                ch += '0';
                if(ch > '9'){
                    ch += 7;
                }
                *p++ = ch;
            }
        }
    }

    *p = 0;

    return str;
}

/*======================================================================*
                               disp_int
 *======================================================================*/
PUBLIC void disp_int(int input)
{
    char output[16];
    itoa(output, input);
    //disp_str(output);
}




/*======================================================================*
                             init_idt_desc
 *----------------------------------------------------------------------*
 初始化 386 中断门
 *======================================================================*/
PUBLIC void init_idt_desc(unsigned char vector, t_8 desc_type, t_pf_int_handler handler, unsigned char privilege)
{
    GATE *  p_gate  = &idt[vector];
    t_32    base    = (t_32)handler;
    p_gate->offset_low  = base & 0xFFFF;
    p_gate->selector    = SELECTOR_KERNEL_CS;
    p_gate->dcount      = 0;
    p_gate->attr        = desc_type | (privilege << 5);
    p_gate->offset_high = (base >> 16) & 0xFFFF;
}

/*======================================================================*
                            init_prot
 *----------------------------------------------------------------------*
 初始化 IDT
 *======================================================================*/
/* 中断向量 */
#define INT_VECTOR_DIVIDE       0x0
#define INT_VECTOR_DEBUG        0x1
#define INT_VECTOR_NMI          0x2
#define INT_VECTOR_BREAKPOINT       0x3
#define INT_VECTOR_OVERFLOW     0x4
#define INT_VECTOR_BOUNDS       0x5
#define INT_VECTOR_INVAL_OP     0x6
#define INT_VECTOR_COPROC_NOT       0x7
#define INT_VECTOR_DOUBLE_FAULT     0x8
#define INT_VECTOR_COPROC_SEG       0x9
#define INT_VECTOR_INVAL_TSS        0xA
#define INT_VECTOR_SEG_NOT      0xB
#define INT_VECTOR_STACK_FAULT      0xC
#define INT_VECTOR_PROTECTION       0xD
#define INT_VECTOR_PAGE_FAULT       0xE
#define INT_VECTOR_COPROC_ERR       0x10

/* 中断向量 */
#define INT_VECTOR_IRQ0         0x20
#define INT_VECTOR_IRQ8         0x28 
  
PUBLIC void init_prot()
{
    init_8259A();

    // 全部初始化成中断门(没有陷阱门)
    init_idt_desc(INT_VECTOR_DIVIDE,    DA_386IGate, divide_error,      PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_DEBUG,     DA_386IGate, single_step_exception, PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_NMI,       DA_386IGate, nmi,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_BREAKPOINT,    DA_386IGate, breakpoint_exception,  PRIVILEGE_USER);
    init_idt_desc(INT_VECTOR_OVERFLOW,  DA_386IGate, overflow,          PRIVILEGE_USER);
    init_idt_desc(INT_VECTOR_BOUNDS,    DA_386IGate, bounds_check,      PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_INVAL_OP,  DA_386IGate, inval_opcode,      PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_COPROC_NOT,    DA_386IGate, copr_not_available,    PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_DOUBLE_FAULT,  DA_386IGate, double_fault,      PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_COPROC_SEG,    DA_386IGate, copr_seg_overrun,      PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_INVAL_TSS, DA_386IGate, inval_tss,         PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_SEG_NOT,   DA_386IGate, segment_not_present,   PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_STACK_FAULT,   DA_386IGate, stack_exception,       PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_PROTECTION,    DA_386IGate, general_protection,    PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_PAGE_FAULT,    DA_386IGate, page_fault,        PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_COPROC_ERR,    DA_386IGate, copr_error,        PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 0,  DA_386IGate, hwint00,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 1,  DA_386IGate, hwint01,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 2,  DA_386IGate, hwint02,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 3,  DA_386IGate, hwint03,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 4,  DA_386IGate, hwint04,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 5,  DA_386IGate, hwint05,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 6,  DA_386IGate, hwint06,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 7,  DA_386IGate, hwint07,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 0,  DA_386IGate, hwint08,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 1,  DA_386IGate, hwint09,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 2,  DA_386IGate, hwint10,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 3,  DA_386IGate, hwint11,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 4,  DA_386IGate, hwint12,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 5,  DA_386IGate, hwint13,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 6,  DA_386IGate, hwint14,           PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 7,  DA_386IGate, hwint15,           PRIVILEGE_KRNL);
}

stdarg.h

#ifndef _STDARG_H
#define _STDARG_H

typedef char *va_list;

/* Amount of space required in an argument list for an arg of type TYPE.
   TYPE may alternatively be an expression whose type is used.  */

#define __va_rounded_size(TYPE)  \
  (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))

#ifndef __sparc__
#define va_start(AP, LASTARG)                       \
 (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#else
#define va_start(AP, LASTARG)                       \
 (__builtin_saveregs (),                        \
  AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#endif

void va_end (va_list);      /* Defined in gnulib */
#define va_end(AP)

#define va_arg(AP, TYPE)                        \
 (AP += __va_rounded_size (TYPE),                   \
  *((TYPE *) (AP - __va_rounded_size (TYPE))))

#endif /* _STDARG_H */

string.h

#ifndef _STRING_H_
#define _STRING_H_

#ifndef NULL
#define NULL ((void *) 0)
#endif

#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned int size_t;
#endif

extern inline int strlen(const char * s){
    int i=0;
    while(s[i] != '\0'){
        i++;
    }
    return i;
}

extern inline char * strcpy(char * dst, const char *src){
    char *ret = dst;
    while ((*dst++=*src++)!='\0');
    return ret;
}
#endif

type.h

#ifndef _TYPE_H_
#define _TYPE_H_

typedef unsigned int        t_32;
typedef unsigned short      t_16;
typedef unsigned char       t_8;
typedef int                 t_bool;
typedef unsigned int        t_port;

typedef void    (*t_pf_int_handler) ();

#endif

vsprintf.c

#include "stdarg.h"
#include "string.h" 

/* we use this so that we can do without the ctype library */
#define is_digit(c) ((c) >= '0' && (c) <= '9')

// 将字符数字转换成整数。输入是数字串指针的指针,返回是结果的数值,另外指针将前移。
static int skip_atoi(const char **s) {
    int i=0;
    while (is_digit(**s)){
        i = i*10 + *((*s)++) - '0';     // 这里导致指针前移
    }
        
    return i;
}

// 定义常用符号常数
#define ZEROPAD 1       /* pad with zero */
#define SIGN    2       /* unsigned/signed long */
#define PLUS    4       /* show plus */
#define SPACE   8       /* space if plus */
#define LEFT    16      /* left justified */
#define SPECIAL 32      /* 0x */
#define SMALL   64      /* use 'abcdef' instead of 'ABCDEF' */

// 除法操作,输入:n为被除数,base为除数;结果:n为商,函数返回值为余数。
#define do_div(n,base) ({ \
int __res; \
__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
__res; })

// 将整数转换为指定进制的字符串。
// 输入:num-整数;base-进制;size-字符串长度;precision-数字长度(精度);type-类型选项。
// 输出:str字符串指针
static char * number(char * str, int num, int base, int size, int precision, int type){
    char c,sign,tmp[36];
    const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int i;

    // 根据类型定义字母集,默认是大写字母
    if (type&SMALL) {
        digits="0123456789abcdefghijklmnopqrstuvwxyz";
    }
    // 如果类型指出要左调整(靠左边界),则屏蔽类型中的填零标志。
    // TODO: 这句没看懂?? 为什么要把最低位置为0
    if (type&LEFT) {
        type &= ~ZEROPAD;
    }
    // 本程序只能处理的进制范围:2-36
    if (base<2 || base>36) {
        return 0;
    }
    // TODO: 这句跟LEFT相关??
    c = (type & ZEROPAD) ? '0' : ' ' ;
    if (type&SIGN && num<0) {
        sign='-';
        num = -num;
    } else{
        sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);
    }
    // 如果是带符号的,字符串宽度就减一
    if (sign) {
        size--;
    }
    if (type&SPECIAL) {
        if (base==16) {
            size -= 2; 
        }   // 16进制需要减2,用于前面的0x
        else if (base==8) {
            size--; 
        }  // 8进制的减1,因为前面的0
    }
    i=0;
    if (num==0){
        tmp[i++]='0';
    } else {
        while (num!=0){
            tmp[i++]=digits[do_div(num,base)];
        }
    }
    
    if (i>precision) {
        precision=i;
    }       // 若字符个数大于精度值,精度值扩展为字符个数
    
    size -= precision;                  // 宽度再减去用于存放数值字符的个数
    // 将转换结果放在str中,如果类型中没有填零(ZEROPAD)和左靠齐标志,
    // 则在str中首先填放剩余宽度值指出的空格数。若需要带符号位,则存入符号
    if (!(type&(ZEROPAD+LEFT))){
        while(size-->0){
            *str++ = ' ';
        }
    }
    if (sign){
        *str++ = sign;
    }
    // 如果是特殊转换的处理,8进制和16进制分别填入0/0x/0X
    if (type&SPECIAL) {
        if (base==8){
            *str++ = '0';
        } else if (base==16) {
            *str++ = '0';
            *str++ = digits[33];    // 'x' or 'X'
        }
    }
    // 如果类型么有左靠齐标志,则在剩余的宽度中存放c字符(‘0’或者空格)
    if (!(type&LEFT)){
        while(size-->0){
            *str++ = c;
        }
    }
    // 若i存有数值num的数字个数,若数字个数小于精度值,则str中放入 精度值-i 个'0'
    while(i<precision--){
        *str++ = '0';
    }
    // 将数值转换好的数字字符填入str中,共i个
    while(i-->0){
        *str++ = tmp[i];
    }
    // 若宽度值仍大于零,则表达类型标志中有左靠齐标志,则在剩余宽度中放入空格
    while(size-->0){
        *str++ = ' ';
    }
    
    return str;
}

// 格式化输出
int vsprintf(char *buf, const char *fmt, va_list args){
    int len;
    int i;
    char * str;     // 用于存放转换过程中的字符串
    char *s;
    int *ip;

    int flags;      /* flags to number() */

    int field_width;    /* width of output field */
    int precision;      /* min. # of digits for integers; max
                   number of chars for from string */
    int qualifier;      /* 'h', 'l', or 'L' for integer fields */

    // 扫描格式字符串,对于不是 % 的就依次存入str
    for (str=buf ; *fmt ; ++fmt) {
        if (*fmt != '%') {
            *str++ = *fmt;
            continue;
        }
        // 取得格式指示字符串中的标志域,并将标志常量放入flags变量中
        /* process flags */
        flags = 0;
        repeat:
            ++fmt;      /* this also skips first '%' */
            switch (*fmt) {
                case '-': flags |= LEFT; goto repeat;
                case '+': flags |= PLUS; goto repeat;
                case ' ': flags |= SPACE; goto repeat;
                case '#': flags |= SPECIAL; goto repeat;
                case '0': flags |= ZEROPAD; goto repeat;
                }
        // 取当前参数数字段宽度域值,放入field_width变量中,如果宽度域中是数值则直接取其为宽度值。
        // 如果宽度域中是字符'*',表示下一个参数指定宽度。因此调用va_arg取宽度值。若此时宽度值
        // 小于0,则该负数表示其带有标志域'-'标志(左靠齐),因此还需要在标志变量中填入该标志,并
        // 将字段宽度值取为其绝对值。
        /* get field width */
        field_width = -1;
        if (is_digit(*fmt))
            field_width = skip_atoi(&fmt);
        else if (*fmt == '*') {
            ++fmt;
            /* it's the next argument */
            field_width = va_arg(args, int);        // 这里有个bug,应插入++fmt。// TODO: 不懂
            if (field_width < 0) {
                field_width = -field_width;
                flags |= LEFT;
            }
        }

        // 取格式转换串的精度域,并放入precision变量中。精度域开始的标志是'.'.
        // 其处理过程与上面宽度域的类似。如果精度域中是数值则直接取其为精度值。如果精度域中是
        // 字符'*',表示下一个参数指定精度。因此调用va_arg取精度值。若此时宽度值小于0,则将
        // 字段精度值取为0.
        /* get the precision */
        precision = -1;
        if (*fmt == '.') {
            ++fmt;  
            if (is_digit(*fmt))
                precision = skip_atoi(&fmt);
            else if (*fmt == '*') {
                /* it's the next argument */
                precision = va_arg(args, int);
            }
            if (precision < 0)
                precision = 0;
        }

        /* get the conversion qualifier */
        qualifier = -1;
        if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
            qualifier = *fmt;
            ++fmt;
        }

        // 分析转换指示符
        switch (*fmt) {
            // ‘c’ 表示对应参数应是字符。此时如果标志域表明不是左靠齐,则该字段前面
            // 放入'宽度域值-1'个空格字符,然后再放入参数字符。如果宽度域大于0,
            // 则表示为左靠齐,则在参数字符后面添加'宽度值-1'个空格字符
        case 'c':
            if (!(flags & LEFT))
                while (--field_width > 0)
                    *str++ = ' ';
            *str++ = (unsigned char) va_arg(args, int);
            while (--field_width > 0)
                *str++ = ' ';
            break;

            // 's'表示对应参数是字符串。首先取参数字符串的长度,若其超过了精度域值,
            // 则扩展精度域=字符串长度。此时如果标志域表明不是左靠齐,则该字段前放入
            // '宽度值-字符串长度'个空格字符。然后再放入参数字符串。如果宽度域还大于0,
            // 则表示为左靠齐,则在参数字符串后面添加'宽度值-字符串长度'个空格字符。
        case 's':
            s = va_arg(args, char *);
            len = strlen(s);
            if (precision < 0)
                precision = len;
            else if (len > precision)
                len = precision;

            if (!(flags & LEFT))
                while (len < field_width--)
                    *str++ = ' ';
            for (i = 0; i < len; ++i)
                *str++ = *s++;
            while (len < field_width--)
                *str++ = ' ';
            break;

            // 'o'表示8进制,通过number函数处理
        case 'o':
            str = number(str, va_arg(args, unsigned long), 8,
                field_width, precision, flags);
            break;

            // 'p'表示一个指针类型,此时若该参数没有设置宽度域,默认宽度域为8
            // 并且需要添零,然后用number函数处理
        case 'p':
            if (field_width == -1) {
                field_width = 8;
                flags |= ZEROPAD;
            }
            str = number(str,
                (unsigned long) va_arg(args, void *), 16,
                field_width, precision, flags);
            break;

            // 'x'-转成16进制
        case 'x':
            flags |= SMALL;
        case 'X':
            str = number(str, va_arg(args, unsigned long), 16,
                field_width, precision, flags);
            break;

            // 'd' & 'i'  表示带符号整数;'u' 表示无符号整数
        case 'd':
        case 'i':
            flags |= SIGN;
        case 'u':
            str = number(str, va_arg(args, unsigned long), 10,
                field_width, precision, flags);
            break;

            // 'n'-表示要把到目前为止转换输出字符数保存到对应参数指针指定的位置中。
            // 首先利用va_arg()取得该参数指针,然后将已经转换好的字符数存到指向的位置
        case 'n':
            ip = va_arg(args, int *);
            *ip = (str - buf);
            break;

            // 若格式转换不是 % ,则表示格式字符串有错,直接将一个“%”写入输出串中。
            // 如果格式转换符的位置还有字符,则也直接将该字符写入输入串中,并返回继续处理
            // 格式字符串,否则表示已经处理到格式字符串的结尾处,退出循环。
        default:
            if (*fmt != '%')
                *str++ = '%';
            if (*fmt)
                *str++ = *fmt;
            else
                --fmt;
            break;
        }
    }
    *str = '\0';        // 字符串结尾字符:'\0'
    return str-buf;     // 返回转换好的长度值
}

// zhangkai
int sprintf(char *buf, const char *fmt, ...){
    va_list args;       // va_list是一个字符指针类型
    int i;

    va_start(args, fmt);
    i=vsprintf(buf,fmt,args);
    va_end(args);
        
    return i;      
}

相关文章

  • 【中断开发:实现时钟中断】(草稿)

    const.h kernel.asm klib.asm protect.h start.c stdarg.h st...

  • Linux C/C++定时器的实现原理和使用方法

    定时器的实现原理 定时器的实现依赖的是CPU时钟中断,时钟中断的精度就决定定时器精度的极限。一个时钟中断源如何实现...

  • 【时钟中断的实现】

    效果 下面那一行,是main函数中的死循环输出变量!右上方的字符是时钟中断服务程序打印的。_start.asm c...

  • 定时器与时钟中断

    时钟中断 时钟中断是一种硬中断,由时间硬件(系统定时器,一种可编程硬件)产生,CPU处理后交由时间中断处理程序来完...

  • 2018-01-10

    自学dsp 。时钟、GPIO、中断

  • 进程切换和系统的一般执行过程

    进程调度的时机 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回...

  • 栈切换Format

    golang实现非协作式抢占调度基于此在线程由于时钟中断从睡眠状态中醒来时,内核栈切换到用户栈执行中断处理函数,可...

  • Keil方法类_中断实验

    中断系统和外部中断 实现一个C51单片机中断需要在中断请求标志位、中断允许标志位和 中断优先级控制位进行组合的实现...

  • 计算机操作系统笔记(四)

    2-3中断机制(SHD) 中断 中断定义 指CPU对突发的反应过程或机制 引入中断的目的 实现并发活动实现实时处理...

  • 软中断的中断处理程序的调用过程

    首先,我们的中断处理程序是通过中断门来实现的,因此,需要在中断描述符表中提前注册好中断描述符。 中断描述符中有段选...

网友评论

      本文标题:【中断开发:实现时钟中断】(草稿)

      本文链接:https://www.haomeiwen.com/subject/orouiktx.html