操作系统真象还原第6章:完善内核

前言

这一章没啥可讲的,主要就是实现一个类似putchar()的函数,当然是使用纯汇编的,书上已经讲的很详细了,我主要加上了一些我自己的注释

代码

print.S

;--------步骤---------------------------
;1.备份寄存器现场
;2.获取光标坐标值,光标坐标值时下一个可打印字符的位置
;3.获取待打印的字符
;4.判断字符是否为控制字符,若是回车符,换行符,退格符三种控制字符之一,则进入
;相应流程,否则,其余字符否被粗暴的认为是可见字符,进入输出流程处理
;5.判断是否需要滚屏
;6.更新光标坐标值,使其指向下一个打印字符的位置
;7.恢复寄存器的现场,退出

TI_GDT equ 0
TI_GDT_RPL0 equ 0
SELECTOR_VIDEO equ (0x0003<<3)+TI_GDT_RPL0

[bits 32]
section .text 
put_int_buffer dq 0
;---------put_str--------------
;打印字符串
;------------------------------
global put_str
put_str:
    push ebx 
    push ecx 
    xor ecx,ecx 
    mov ebx,[esp+12] 

.goon:
    mov cl,[ebx]
    cmp cl,0 
    jz .str_over 
    push ecx 
    call put_char 
    add esp,4 
    inc ebx 
    jmp .goon 
.str_over:
    pop ecx 
    pop ebx 
    ret 
;------------put_char-----------
;把栈中一个字符写入光标所在处
;------------------------------
global put_char
put_char:
    pushad  ;备份
    mov ax,SELECTOR_VIDEO
    mov gs,ax 
    
    mov dx,0x03d4   ;端口为0x03d4
    mov al,0x0e     ;得到光标的高8位地址
    out dx,al       ;写入端口,得到索引
    mov dx,0x03d5   ;端口为0x03d5
    in al,dx        ;读到光标的高8位
    mov ah,al 

    mov dx,0x03d4
    mov al,0x0f     ;光标的低8位
    out dx,al 
    mov dx,0x03d5 
    in al,dx 

    mov bx,ax          ;将光标存入bx中
    ;下面这行获取待打印的字符
    mov ecx,[esp+36]  ;pushad 占用了32字节,加上主调函数4字节的返回地址,36字节
    
    cmp cl,0xd          ;回车 ascii码
    jz .is_carriage_return
    cmp cl,0xa          ;换行
    jz .is_line_feed 
    
    cmp cl,0x8          ;空格
    jz .is_backspace 
    jmp .put_other

.is_backspace:
    
    dec bx      ;bx存储着光标的地址,减一就是前一个,
    shl bx,1    ;左移是因为显存中是用两个字节来存储字符的
    
    mov byte [gs:bx],0x20          ;删除的字符复盖为0或者空格
    inc bx 
    mov byte [gs:bx],0x07
    shr bx,1
    jmp .set_cursor

.put_other:
    
    shl bx,1
    
    mov [gs:bx],cl ;将字符输入到显存中
    inc bx 
    mov byte [gs:bx],0x07 ;背景色
    shr bx,1              ;右移动,下一个位置
    inc bx 
    cmp bx,2000
    jl .set_cursor 

.is_line_feed: 
.is_carriage_return:
    
    xor dx,dx   ;清零
    mov ax,bx 
    mov si,80
    div si 
    sub bx,dx  ;减去除以80的余数,再加上80就是下一行

.is_carriage_return_end:
    add bx,80 
    cmp bx,2000 
.is_line_feed_end:
    jl .set_cursor  ;小于2000个字符就设置光标,否则往下执行

.roll_screen:
    cld  ;清除标志位
    mov ecx,960     ;每次复制4字节,一共3840字节,(2000-80)*2,所以有960次,
    
    mov esi,0xc00b80a0 ;1行的起始位置
    mov edi,0xc00b8000 ;0行的起始位置
    rep movsd          ;目的地址 edi,源地址 esi,大数据复制,由源地址复制到目的地址

    mov ebx,3840       ;指向最后一行
    mov ecx,80         ;循环次数80次,因为要把最后一行的字符都清除,每次清除1个字符

.cls:
    mov word [gs:ebx],0x0720 ;空格
    add ebx,2                ;每个字符占两字节
    loop .cls 
    mov bx,1920              ;回到最后一行的首字符,如此达到了回滚

.set_cursor:
    mov dx,0x03d4 
    mov al,0x0e 
    out dx,al 
    mov dx,0x03d5 
    mov al,bh 
    out dx,al 

    mov dx,0x03d4 
    mov al,0x0f 
    out dx,al 
    mov dx,0x03d5 
    mov al,bl
    out dx,al 
.put_char_done:
    popad   ;恢复32字节
    ret     ;返回是弹出4字节,所以正好弹出了36字节

;-------整数打印--------------------
;put_int
;-----------------------------------
global put_int
put_int:
    pushad
    mov ebp,esp 
    mov eax,[ebp+4*9] ;把第一个参数给eax
    mov edx,eax       ;edx存储参数,也就是数字
    mov edi,7         ;put_int_buffer的最后一个字节索引,从0开始
    mov ecx,8         ;重复次数
    mov ebx,put_int_buffer

.16based_4bits:
    and edx,0x0000000f ;数字的末尾4位
    
    cmp edx,9          ;进行转化
    jg .is_A2F
    add edx,'0'
    jmp .store 

.is_A2F:
    sub edx,10
    add edx,'A'

.store:
    mov [ebx+edi],dl ;将转化后的字符存储在put_buffer_int中,此时是大端序
    dec edi 
    shr eax,4 
    mov edx,eax 
    loop .16based_4bits

.ready_to_print:
    inc edi
.skip_prefix_0:
    cmp edi,8
    je .full0 

.go_on_skip:
    mov cl,[put_int_buffer+edi]
    inc edi 
    cmp cl,'0'
    je .skip_prefix_0 ;消除掉前缀0
    dec edi 
    jmp .put_each_num 

.full0:
    mov cl,'0'
.put_each_num:
    push ecx 
    call put_char ;进行打印
    add esp,4 
    inc edi 
    mov cl,[put_int_buffer+edi]
    cmp edi,8 
    jl .put_each_num
    popad
    ret

main.c

;;主要测试一下会不会回滚
#include "print.h"
int main(void){
    put_char('r');
    put_char('e');
    put_char('t');
    put_char('u');
    put_char('r');
    put_char('n');
    put_char('\n');
    put_char('1');
    put_char('2');
    put_char('3');
    put_char('4');
    put_char('5');
    put_char('\b');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_str("hello kernel!\n");
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_char('\n');
    put_int(0x01);
    put_char('\n');
    put_int(0x00);
    while(1);
    return 0;
}

相关命令

nasm -f elf -o lib/kernel/print.o lib/kernel/print.S
gcc -m32 -I lib/kernel/ -c -o lib/kernel/main.o lib/kernel/main.c
ld -m elf_i386 -Ttext 0xc0001500 -e main -o lib/kernel/kernel.bin \
lib/kernel/main.o lib/kernel/print.o

结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值