printf 使用過程示意圖
程式碼
include/const.h (修改)
#define NR_SYS_CALL 2
include/global.h (新增)
extern TASK user_proc_table[];
include/proc.h (修改)
typedef struct s_proc {
STACK_FRAME regs; /* process registers saved in stack frame */
u16 ldt_sel; /* gdt selector giving ldt base and limit */
DESCRIPTOR ldts[LDT_SIZE]; /* local descriptors for code and data */
int ticks; /* remained ticks */
int priority;
u32 pid; /* process id passed in from MM */
char p_name[16]; /* name of the process */
int nr_tty; /* 用以指定此處理序的 TTY */
} PROCESS;
/* Number of tasks & procs */
#define NR_TASKS 1
#define NR_PROCS 3
include/string.h (新增)
PUBLIC int strlen(char* p_str);
include/type.h (新增)
typedef char * va_list;
kernel/proc.c (修改)
for (p = proc_table; p < proc_table+NR_TASKS+NR_PROCS; p++) {
for(p=proc_table;p < proc_table+NR_TASKS+NR_PROCS;p++) {
kernel/protect.c (修改)
for (i = 0; i < NR_TASKS+NR_PROCS; i++){
include/proto.h
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
proto.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* klib.asm */
PUBLIC void out_byte(u16 port, u8 value);
PUBLIC u8 in_byte(u16 port);
PUBLIC void disp_str(char * info);
PUBLIC void disp_color_str(char * info, int color);
/* protect.c */
PUBLIC void init_prot();
PUBLIC u32 seg2phys(u16 seg);
/* klib.c */
PUBLIC void delay(int time);
/* kernel.asm */
void restart();
/* main.c */
void TestA();
void TestB();
void TestC();
/* i8259.c */
PUBLIC void put_irq_handler(int irq, irq_handler handler);
PUBLIC void spurious_irq(int irq);
/* clock.c */
PUBLIC void clock_handler(int irq);
PUBLIC void init_clock();
/* keyboard.c */
PUBLIC void init_keyboard();
/* tty.c */
PUBLIC void task_tty();
PUBLIC void in_process(TTY* p_tty, u32 key);
/* console.c */
PUBLIC void out_char(CONSOLE* p_con, char ch);
PUBLIC void scroll_screen(CONSOLE* p_con, int direction);
/* printf.c */
PUBLIC int printf(const char *fmt, ...);
/* vsprintf.c */
PUBLIC int vsprintf(char *buf, const char *fmt, va_list args);
/* 以下是系統調用相關 */
/* 系統調用 - 系統級 */
/* proc.c */
PUBLIC int sys_get_ticks();
PUBLIC int sys_write(char* buf, int len, PROCESS* p_proc);
/* syscall.asm */
PUBLIC void sys_call(); /* int_handler */
/* 系統調用 - 用戶級 */
PUBLIC int get_ticks();
PUBLIC void write(char* buf, int len);
kernel/console.c
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
console.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*
迴車鍵: 把光標移到第一列
換行鍵: 把光標前進到下一行
*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
PRIVATE void set_cursor(unsigned int position);
PRIVATE void set_video_start_addr(u32 addr);
PRIVATE void flush(CONSOLE* p_con);
/*======================================================================*
init_screen
*======================================================================*/
PUBLIC void init_screen(TTY* p_tty)
{
int nr_tty = p_tty - tty_table;
p_tty->p_console = console_table + nr_tty;
int v_mem_size = V_MEM_SIZE >> 1; /* 顯存總大小 (in WORD) */
int con_v_mem_size = v_mem_size / NR_CONSOLES;
p_tty->p_console->original_addr = nr_tty * con_v_mem_size;
p_tty->p_console->v_mem_limit = con_v_mem_size;
p_tty->p_console->current_start_addr = p_tty->p_console->original_addr;
/* 默認光標位置在最開始處 */
p_tty->p_console->cursor = p_tty->p_console->original_addr;
if (nr_tty == 0) {
/* 第一個控制台沿用原來的光標位置 */
p_tty->p_console->cursor = disp_pos / 2;
disp_pos = 0;
}
else {
out_char(p_tty->p_console, nr_tty + '0');
out_char(p_tty->p_console, '#');
}
set_cursor(p_tty->p_console->cursor);
}
/*======================================================================*
is_current_console
*======================================================================*/
PUBLIC int is_current_console(CONSOLE* p_con)
{
return (p_con == &console_table[nr_current_console]);
}
/*======================================================================*
out_char
*======================================================================*/
PUBLIC void out_char(CONSOLE* p_con, char ch)
{
u8* p_vmem = (u8*)(V_MEM_BASE + p_con->cursor * 2);
switch(ch) {
case '\n':
if (p_con->cursor < p_con->original_addr +
p_con->v_mem_limit - SCREEN_WIDTH) {
p_con->cursor = p_con->original_addr + SCREEN_WIDTH *
((p_con->cursor - p_con->original_addr) /
SCREEN_WIDTH + 1);
}
break;
case '\b':
if (p_con->cursor > p_con->original_addr) {
p_con->cursor--;
*(p_vmem-2) = ' ';
*(p_vmem-1) = DEFAULT_CHAR_COLOR;
}
break;
default:
if (p_con->cursor <
p_con->original_addr + p_con->v_mem_limit - 1) {
*p_vmem++ = ch;
*p_vmem++ = DEFAULT_CHAR_COLOR;
p_con->cursor++;
}
break;
}
while (p_con->cursor >= p_con->current_start_addr + SCREEN_SIZE) {
scroll_screen(p_con, SCR_DN);
}
flush(p_con);
}
/*======================================================================*
flush
*======================================================================*/
PRIVATE void flush(CONSOLE* p_con)
{
if (is_current_console(p_con)) {
set_cursor(p_con->cursor);
set_video_start_addr(p_con->current_start_addr);
}
}
/*======================================================================*
set_cursor
*======================================================================*/
PRIVATE void set_cursor(unsigned int position)
{
disable_int();
out_byte(CRTC_ADDR_REG, CURSOR_H);
out_byte(CRTC_DATA_REG, (position >> 8) & 0xFF);
out_byte(CRTC_ADDR_REG, CURSOR_L);
out_byte(CRTC_DATA_REG, position & 0xFF);
enable_int();
}
/*======================================================================*
set_video_start_addr
*======================================================================*/
PRIVATE void set_video_start_addr(u32 addr)
{
disable_int();
out_byte(CRTC_ADDR_REG, START_ADDR_H);
out_byte(CRTC_DATA_REG, (addr >> 8) & 0xFF);
out_byte(CRTC_ADDR_REG, START_ADDR_L);
out_byte(CRTC_DATA_REG, addr & 0xFF);
enable_int();
}
/*======================================================================*
select_console
*======================================================================*/
PUBLIC void select_console(int nr_console) /* 0 ~ (NR_CONSOLES - 1) */
{
if ((nr_console < 0) || (nr_console >= NR_CONSOLES)) {
return;
}
nr_current_console = nr_console;
flush(&console_table[nr_console]);
}
/*======================================================================*
scroll_screen
*----------------------------------------------------------------------*
滾屏.
*----------------------------------------------------------------------*
direction:
SCR_UP : 向上滾屏
SCR_DN : 向下滾屏
其它 : 不做處理
*======================================================================*/
PUBLIC void scroll_screen(CONSOLE* p_con, int direction)
{
if (direction == SCR_UP) {
if (p_con->current_start_addr > p_con->original_addr) {
p_con->current_start_addr -= SCREEN_WIDTH;
}
}
else if (direction == SCR_DN) {
if (p_con->current_start_addr + SCREEN_SIZE <
p_con->original_addr + p_con->v_mem_limit) {
p_con->current_start_addr += SCREEN_WIDTH;
}
}
else{
}
flush(p_con);
}
kernel/global.c
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
global.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#define GLOBAL_VARIABLES_HERE
#include "type.h"
#include "const.h"
#include "protect.h"
#include "tty.h"
#include "console.h"
#include "proc.h"
#include "global.h"
#include "proto.h"
PUBLIC PROCESS proc_table[NR_TASKS + NR_PROCS];
PUBLIC TASK task_table[NR_TASKS] = {
{task_tty, STACK_SIZE_TTY, "tty"}};
PUBLIC TASK user_proc_table[NR_PROCS] = {
{TestA, STACK_SIZE_TESTA, "TestA"},
{TestB, STACK_SIZE_TESTB, "TestB"},
{TestC, STACK_SIZE_TESTC, "TestC"}};
PUBLIC char task_stack[STACK_SIZE_TOTAL];
PUBLIC TTY tty_table[NR_CONSOLES];
PUBLIC CONSOLE console_table[NR_CONSOLES];
PUBLIC irq_handler irq_table[NR_IRQ];
PUBLIC system_call sys_call_table[NR_SYS_CALL] = {sys_get_ticks, sys_write};
kernel/kernel.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; kernel.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%include "sconst.inc"
; 導入函數
extern cstart
extern kernel_main
extern exception_handler
extern spurious_irq
extern clock_handler
extern disp_str
extern delay
extern irq_table
; 導入全局變量
extern gdt_ptr
extern idt_ptr
extern p_proc_ready
extern tss
extern disp_pos
extern k_reenter
extern sys_call_table
bits 32
[SECTION .data]
clock_int_msg db "^", 0
[SECTION .bss]
StackSpace resb 2 * 1024
StackTop: ; 堆疊頂
[section .text] ; 程式碼在此
global _start ; 導出 _start
global restart
global sys_call
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 段中
mov dword [disp_pos], 0
sgdt [gdt_ptr] ; cstart() 中將會用到 gdt_ptr
call cstart ; 在此函數中改變了gdt_ptr,讓它指向新的GDT
lgdt [gdt_ptr] ; 使用新的GDT
lidt [idt_ptr]
jmp SELECTOR_KERNEL_CS:csinit
csinit: ; “這個跳轉指令強制使用剛剛初始化的結構”——<<OS:D&I 2nd>> P90.
;jmp 0x40:0
;ud2
xor eax, eax
mov ax, SELECTOR_TSS
ltr ax
;sti
jmp kernel_main
;hlt
; 中斷和異常 -- 硬件中斷
; ---------------------------------
%macro hwint_master 1
call save
in al, INT_M_CTLMASK ; `.
or al, (1 << %1) ; | 屏蔽當前中斷
out INT_M_CTLMASK, al ; /
mov al, EOI ; `. 置EOI位
out INT_M_CTL, al ; /
sti ; CPU在響應中斷的過程中會自動關中斷,這句之後就允許響應新的中斷
push %1 ; `.
call [irq_table + 4 * %1] ; | 中斷處理程序
pop ecx ; /
cli
in al, INT_M_CTLMASK ; `.
and al, ~(1 << %1) ; | 恢復接受當前中斷
out INT_M_CTLMASK, al ; /
ret
%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
; ====================================================================================
; save
; ====================================================================================
save:
pushad ; `.
push ds ; |
push es ; | 保存原暫存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx
mov esi, esp ;esi = 進程表起始位址
inc dword [k_reenter] ;k_reenter++;
cmp dword [k_reenter], 0 ;if(k_reenter ==0)
jne .1 ;{
mov esp, StackTop ; mov esp, StackTop <--切換到內核堆疊
push restart ; push restart
jmp [esi + RETADR - P_STACKBASE]; return;
.1: ;} else { 已經在內核堆疊,不需要再切換
push restart_reenter ; push restart_reenter
jmp [esi + RETADR - P_STACKBASE]; return;
;}
; ====================================================================================
; sys_call
; ====================================================================================
sys_call:
call save
push dword [p_proc_ready]
sti
push ecx
push ebx
call [sys_call_table + eax * 4]
add esp, 4 * 3
mov [esi + EAXREG - P_STACKBASE], eax
cli
ret
; ====================================================================================
; restart
; ====================================================================================
restart:
mov esp, [p_proc_ready]
lldt [esp + P_LDT_SEL]
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax
restart_reenter:
dec dword [k_reenter]
pop gs
pop fs
pop es
pop ds
popad
add esp, 4
iretd
kernel/main.c
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
main.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"
/*======================================================================*
kernel_main
*======================================================================*/
PUBLIC int kernel_main()
{
disp_str("-----\"kernel_main\" begins-----\n");
TASK* p_task = task_table;
PROCESS* p_proc = proc_table;
char* p_task_stack = task_stack + STACK_SIZE_TOTAL;
u16 selector_ldt = SELECTOR_LDT_FIRST;
int i;
u8 privilege;
u8 rpl;
int eflags;
for (i = 0; i < NR_TASKS+NR_PROCS; i++) {
if (i < NR_TASKS) { /* 任務 */
p_task = task_table + i;
privilege = PRIVILEGE_TASK;
rpl = RPL_TASK;
eflags = 0x1202; /* IF=1, IOPL=1, bit 2 is always 1 */
}
else { /* 用戶進程 */
p_task = user_proc_table + (i - NR_TASKS);
privilege = PRIVILEGE_USER;
rpl = RPL_USER;
eflags = 0x202; /* IF=1, bit 2 is always 1 */
}
strcpy(p_proc->p_name, p_task->name); // name of the process
p_proc->pid = i; // pid
p_proc->ldt_sel = selector_ldt;
memcpy(&p_proc->ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3],
sizeof(DESCRIPTOR));
p_proc->ldts[0].attr1 = DA_C | privilege << 5;
memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3],
sizeof(DESCRIPTOR));
p_proc->ldts[1].attr1 = DA_DRW | privilege << 5;
p_proc->regs.cs = (0 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
p_proc->regs.ds = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
p_proc->regs.es = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
p_proc->regs.fs = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
p_proc->regs.ss = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
p_proc->regs.gs = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | rpl;
p_proc->regs.eip = (u32)p_task->initial_eip;
p_proc->regs.esp = (u32)p_task_stack;
p_proc->regs.eflags = eflags;
p_proc->nr_tty = 0;
p_task_stack -= p_task->stacksize;
p_proc++;
p_task++;
selector_ldt += 1 << 3;
}
proc_table[0].ticks = proc_table[0].priority = 15;
proc_table[1].ticks = proc_table[1].priority = 5;
proc_table[2].ticks = proc_table[2].priority = 5;
proc_table[3].ticks = proc_table[3].priority = 5;
proc_table[1].nr_tty = 0;
proc_table[2].nr_tty = 1;
proc_table[3].nr_tty = 1;
k_reenter = 0;
ticks = 0;
p_proc_ready = proc_table;
init_clock();
init_keyboard();
restart();
while(1){}
}
/*======================================================================*
TestA
*======================================================================*/
void TestA()
{
int i = 0;
while (1) {
printf("<Ticks:%x>", get_ticks());
milli_delay(200);
}
}
/*======================================================================*
TestB
*======================================================================*/
void TestB()
{
int i = 0x1000;
while(1){
printf("B");
milli_delay(200);
}
}
/*======================================================================*
TestB
*======================================================================*/
void TestC()
{
int i = 0x2000;
while(1){
printf("C");
milli_delay(200);
}
}
kernel/printf.c
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
printf.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
/******************************************************************************************
可變參數函數調用原理(其中涉及的數字皆為舉例)
===========================================================================================
i = 0x23;
j = 0x78;
char fmt[] = "%x%d";
printf(fmt, i, j);
push j
push i
push fmt
call printf
add esp, 3 * 4
┃ HIGH ┃ ┃ HIGH ┃
┃ ... ┃ ┃ ... ┃
┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫
┃ ┃ 0x32010┃ '\0' ┃
┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫
0x3046C┃ 0x78 ┃ 0x3200c┃ d ┃
┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫
arg = 0x30468┃ 0x23 ┃ 0x32008┃ % ┃
┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫
0x30464┃ 0x32000 ───╂────┐ 0x32004┃ x ┃
┣━━━━━━━━━━┫ │ ┣━━━━━━━━━━┫
┃ ┃ └──→ 0x32000┃ % ┃
┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫
┃ ... ┃ ┃ ... ┃
┃ LOW ┃ ┃ LOW ┃
實際上,調用 vsprintf 的情形是這樣的:
vsprintf(buf, 0x32000, 0x30468);
******************************************************************************************/
/*======================================================================*
printf
*======================================================================*/
int printf(const char *fmt, ...)
{
int i;
char buf[256];
va_list arg = (va_list)((char*)(&fmt) + 4); /*4是參數fmt所占堆棧中的大小*/
i = vsprintf(buf, fmt, arg);
write(buf, i);
return i;
}
kernel/syscall.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; syscall.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%include "sconst.inc"
INT_VECTOR_SYS_CALL equ 0x90
_NR_get_ticks equ 0
_NR_write equ 1
; 導出符號
global get_ticks
global write
bits 32
[section .text]
; ====================================================================
; get_ticks
; ====================================================================
get_ticks:
mov eax, _NR_get_ticks
int INT_VECTOR_SYS_CALL
ret
; ====================================================================================
; void write(char* buf, int len);
; ====================================================================================
write:
mov eax, _NR_write
mov ebx, [esp + 4]
mov ecx, [esp + 8]
int INT_VECTOR_SYS_CALL
ret
kernel/tty.c
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tty.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
#define TTY_FIRST (tty_table)
#define TTY_END (tty_table + NR_CONSOLES)
PRIVATE void init_tty(TTY* p_tty);
PRIVATE void tty_do_read(TTY* p_tty);
PRIVATE void tty_do_write(TTY* p_tty);
PRIVATE void put_key(TTY* p_tty, u32 key);
/*======================================================================*
task_tty
*======================================================================*/
PUBLIC void task_tty()
{
TTY* p_tty;
init_keyboard();
for (p_tty=TTY_FIRST;p_tty<TTY_END;p_tty++) {
init_tty(p_tty);
}
select_console(0);
while (1) {
for (p_tty=TTY_FIRST;p_tty<TTY_END;p_tty++) {
tty_do_read(p_tty);
tty_do_write(p_tty);
}
}
}
/*======================================================================*
init_tty
*======================================================================*/
PRIVATE void init_tty(TTY* p_tty)
{
p_tty->inbuf_count = 0;
p_tty->p_inbuf_head = p_tty->p_inbuf_tail = p_tty->in_buf;
init_screen(p_tty);
}
/*======================================================================*
in_process
*======================================================================*/
PUBLIC void in_process(TTY* p_tty, u32 key)
{
char output[2] = {'\0', '\0'};
if (!(key & FLAG_EXT)) {
put_key(p_tty, key);
}
else {
int raw_code = key & MASK_RAW;
switch(raw_code) {
case ENTER:
put_key(p_tty, '\n');
break;
case BACKSPACE:
put_key(p_tty, '\b');
break;
case UP:
if ((key & FLAG_SHIFT_L) || (key & FLAG_SHIFT_R)) {
scroll_screen(p_tty->p_console, SCR_DN);
}
break;
case DOWN:
if ((key & FLAG_SHIFT_L) || (key & FLAG_SHIFT_R)) {
scroll_screen(p_tty->p_console, SCR_UP);
}
break;
case F1:
case F2:
case F3:
case F4:
case F5:
case F6:
case F7:
case F8:
case F9:
case F10:
case F11:
case F12:
/* Alt + F1~F12 */
if ((key & FLAG_ALT_L) || (key & FLAG_ALT_R)) {
select_console(raw_code - F1);
}
break;
default:
break;
}
}
}
/*======================================================================*
put_key
*======================================================================*/
PRIVATE void put_key(TTY* p_tty, u32 key)
{
if (p_tty->inbuf_count < TTY_IN_BYTES) {
*(p_tty->p_inbuf_head) = key;
p_tty->p_inbuf_head++;
if (p_tty->p_inbuf_head == p_tty->in_buf + TTY_IN_BYTES) {
p_tty->p_inbuf_head = p_tty->in_buf;
}
p_tty->inbuf_count++;
}
}
/*======================================================================*
tty_do_read
*======================================================================*/
PRIVATE void tty_do_read(TTY* p_tty)
{
if (is_current_console(p_tty->p_console)) {
keyboard_read(p_tty);
}
}
/*======================================================================*
tty_do_write
*======================================================================*/
PRIVATE void tty_do_write(TTY* p_tty)
{
if (p_tty->inbuf_count) {
char ch = *(p_tty->p_inbuf_tail);
p_tty->p_inbuf_tail++;
if (p_tty->p_inbuf_tail == p_tty->in_buf + TTY_IN_BYTES) {
p_tty->p_inbuf_tail = p_tty->in_buf;
}
p_tty->inbuf_count--;
out_char(p_tty->p_console, ch);
}
}
/*======================================================================*
tty_write
*======================================================================*/
PUBLIC void tty_write(TTY* p_tty, char* buf, int len)
{
char* p = buf;
int i = len;
while (i) {
out_char(p_tty->p_console, *p++);
i--;
}
}
/*======================================================================*
sys_write
*======================================================================*/
PUBLIC int sys_write(char* buf, int len, PROCESS* p_proc)
{
tty_write(&tty_table[p_proc->nr_tty], buf, len);
return 0;
}
kernel/vsprintf.c
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
vsprintf.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "string.h"
/*
* 為更好地理解此函數的原理,可參考 printf 的注釋部分。
*/
/*======================================================================*
vsprintf
*======================================================================*/
int vsprintf(char *buf, const char *fmt, va_list args)
{
char* p;
char tmp[256];
va_list p_next_arg = args;
for (p=buf;*fmt;fmt++) {
if (*fmt != '%') {
*p++ = *fmt;
continue;
}
fmt++;
switch (*fmt) {
case 'x':
itoa(tmp, *((int*)p_next_arg));
strcpy(p, tmp);
p_next_arg += 4;
p += strlen(tmp);
break;
case 's':
break;
default:
break;
}
}
return (p - buf);
}
lib/string.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; string.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
[SECTION .text]
; 導出函數
global memcpy
global memset
global strcpy
global strlen
; ------------------------------------------------------------------------
; void* memcpy(void* es:p_dst, void* ds:p_src, int size);
; ------------------------------------------------------------------------
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 memset(void* p_dst, char ch, int size);
; ------------------------------------------------------------------------
memset:
push ebp
mov ebp, esp
push esi
push edi
push ecx
mov edi, [ebp + 8] ; Destination
mov edx, [ebp + 12] ; Char to be putted
mov ecx, [ebp + 16] ; Counter
.1:
cmp ecx, 0 ; 判斷計數器
jz .2 ; 計數器為零時跳出
mov byte [edi], dl ; ┓
inc edi ; ┛
dec ecx ; 計數器減一
jmp .1 ; 循環
.2:
pop ecx
pop edi
pop esi
mov esp, ebp
pop ebp
ret ; 函數結束,返回
; ------------------------------------------------------------------------
; ------------------------------------------------------------------------
; char* strcpy(char* p_dst, char* p_src);
; ------------------------------------------------------------------------
strcpy:
push ebp
mov ebp, esp
mov esi, [ebp + 12] ; Source
mov edi, [ebp + 8] ; Destination
.1:
mov al, [esi] ; ┓
inc esi ; ┃
; ┣ 逐字元移動
mov byte [edi], al ; ┃
inc edi ; ┛
cmp al, 0 ; 是否遇到 '\0'
jnz .1 ; 沒遇到就繼續循環,遇到就結束
mov eax, [ebp + 8] ; 返回值
pop ebp
ret ; 函數結束,返回
; strcpy 結束-------------------------------------------------------------
; ------------------------------------------------------------------------
; int strlen(char* p_str);
; ------------------------------------------------------------------------
strlen:
push ebp
mov ebp, esp
mov eax, 0 ; 字元串長度開始是 0
mov esi, [ebp + 8] ; esi 指向首位址
.1:
cmp byte [esi], 0 ; 看 esi 指向的字元是否是 '\0'
jz .2 ; 如果是 '\0',程序結束
inc esi ; 如果不是 '\0',esi 指向下一個字元
inc eax ; 並且,eax 自加一
jmp .1 ; 如此循環
.2:
pop ebp
ret ; 函數結束,返回
; ------------------------------------------------------------------------
Makefile (新增)
kernel/printf.o: kernel/printf.c
$(CC) $(CFLAGS) -o $@ $<
kernel/vsprintf.o: kernel/vsprintf.c
$(CC) $(CFLAGS) -o $@ $<
Makefile (修改)
OBJS = kernel/kernel.o kernel/syscall.o kernel/start.o kernel/main.o\
kernel/clock.o kernel/keyboard.o kernel/tty.o kernel/console.o\
kernel/i8259.o kernel/global.o kernel/protect.o kernel/proc.o\
kernel/printf.o kernel/vsprintf.o\
lib/kliba.o lib/klib.o lib/string.o
目錄結構
.
├── a.img
├── boot
│ ├── boot.asm
│ ├── include
│ │ ├── fat12hdr.inc
│ │ ├── load.inc
│ │ └── pm.inc
│ └── loader.asm
├── include
│ ├── console.h
│ ├── const.h
│ ├── global.h
│ ├── keyboard.h
│ ├── keymap.h
│ ├── proc.h
│ ├── protect.h
│ ├── proto.h
│ ├── sconst.inc
│ ├── string.h
│ ├── tty.h
│ └── type.h
├── kernel
│ ├── clock.c
│ ├── console.c
│ ├── global.c
│ ├── i8259.c
│ ├── kernel.asm
│ ├── keyboard.c
│ ├── main.c
│ ├── printf.c
│ ├── proc.c
│ ├── protect.c
│ ├── start.c
│ ├── syscall.asm
│ ├── tty.c
│ └── vsprintf.c
├── lib
│ ├── kliba.asm
│ ├── klib.c
│ └── string.asm
└── Makefile
執行結果
留言
張貼留言