效果
效果下面那一行,是main函数中的死循环输出变量!
右上方的字符是时钟中断服务程序打印的。
_start.asm
SELECTOR_KERNEL_CS equ 8
; 导入函数
extern cstart
extern main
; 导入全局变量
extern gdt_ptr
extern idt_ptr
extern c_cs;
extern c_ds;
extern c_es;
extern c_ss;
; 导出 _start
global _start
[SECTION .bss]
StackSpace resb 2 * 1024
StackTop: ; 栈顶
[section .text] ; 代码在此
_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() 调用的relocate_gdt中将会用到 gdt_ptr
mov word[c_cs], cs
mov word[c_ds], ds
mov word[c_es], es
mov word[c_ss], ss
call cstart ; 在此函数中改变了gdt_ptr,让它指向新的GDT
lgdt [gdt_ptr] ; 使用新的GDT
lidt [idt_ptr] ; 使用IDT
mov ah, 0x74 ;灰底红字
mov al, 'I'
mov [gs:((80*0 + 70)*2)], ax
sti
call main ; 重新放置好GDT后,重新进入main
;ud2
jmp $
jmp $
jmp $
jmp $
jmp $
jmp $
jmp $
jmp $
jmp $
jmp $
jmp $
jmp $
jmp $
cstart.c中的main
// 设置完GDT、IDT后,进入main
PUBLIC void main(){
uint32 num = 0;
while(1){
for(int i=0; i<80; i++){
print_str_fix_pos((80*24+i)*2, " ");
}
++num;
sprintf(buf, "[main], num is %d\n", num);
print_str_fix_pos((80*24+0)*2, buf);
}
}
interrupt.c中初始化8259A
PRIVATE void init_8259A(){
out_byte(INT_M_CTL, 0x11); // Master 8259, ICW1. == 0001 0001
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'. 0000 0100
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_M_CTLMASK, 0xFE); // Master 8259, OCW1. 只打开了时钟中断
out_byte(INT_S_CTLMASK, 0xFF); // Slave 8259, OCW1.
}
时钟中断服务程序!
[section .text] ; 代码在此
ALIGN 16
hwint00: ; Interrupt routine for irq 0 (the clock).
pushad
push ds
push es
push fs
push gs
inc byte [gs:((80*0 + 70)*2)]
mov al, 20h
out 20h, al
;恢复源寄存器的内容
pop gs
pop fs
pop es
pop ds
popad
iretd
网友评论