Welcome to Yu-Xi|

宇星海

园龄:2年5个月粉丝:0关注:6

2024-12-21 20:13阅读: 18评论: 0推荐: 0

《操作系统真相还原》实验记录2.1——print.S打印函数

一、print.S文件说明

  1. put_char 函数(每次只打印一个字符)是各种打印函数的核心

1.1 功能说明

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

1.2 原理说明

  1. put_char 的打印原理是直接写显存;
  2. 在 32 位保护模式下对内存的操作是“段基址(选择子):段内偏移量”,因此需要使用到视频段选择子,我们使用段寄存器 gs 来存储视频段选择子。

二、前置知识点

  1. 显卡操作只用到了CRT Controller Registers 分组中的寄存器;
  2. CRT controller 寄存器组的 Address Register 的端口地址默认为0x03D4,Data Register 的端口地址0x03D5。
  3. 计算机工程师把数据结构中数组的知识用到了硬件中。他们把每一个寄存器分组视为一个寄存器数组,提供一个寄存器用于指定数组下标,再提供一个寄存器用于对索引所指向的数组元素(也就是寄存器)进行输入输出操作。这样用这两个寄存器就能够定位寄存器数组中的任何寄存器。
  4. 这两个寄存器就是各组中的 Address Register 和 Data Register。
    1. Address Register 作为数组的索引(下标);
    2. Data Register 作为寄存器数组中该索引对应的寄存器,它相当于所对应的寄存器的窗口,往此窗口读写的数据都作用在索引所对应的寄存器上;
    3. 对这类分组的寄存器操作方法是先在 Address Register 中指定寄存器的索引值,用来确定所操作的寄存器是哪个,然后在Data Register 寄存器中对所索引的寄存器进行读写操作;

三、代码展示

汇编代码

TI_GDT equ 0
RPL0 equ 0
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ;create SELECTOR_VIDEO.

section .data
put_int_buffer dq 0  ;this buffer is used to translate int to char

[bits 32]
section .text
;---------------------put_char--------------
;The function's behavior:write one char from stack to the address where the cursor point.
;-------------------------------------------
global put_char ;extern document can invoke this function.
put_char:
    pushad   ;pushad:push all double.Back the environment of all "double byte registers" in 32 mode(totlally have numbers of 8).
    mov ax, SELECTOR_VIDEO
    mov gs, ax    ;we can't push number to fragment register directly.

;-----------get the cursor's address--------
;first: get the high 8bit.
    mov dx, 0x03d4
    mov al, 0x0e  ;Cursor Location High Register
    out dx, al   ;designate offset of register group.
    mov dx, 0x03d5
    in al, dx
    mov ah, al
;second: get the low 8bit.
    mov dx, 0x03d4
    mov al, 0x0f   ;Cursor Location Low Register
    out dx, al
    mov dx, 0x03d5
    in al, dx
;store cursor's address to register of bx.
    mov bx, ax
;get the string we want to print.
    mov ecx, [esp + 36] ;pushad us 4 × 8 = 32byte,the return address use 4byte,so the string's address is under esp + 36.

    cmp cl, 0xd  ;Carriage Return's ASCLL is 0xd
    jz .is_carriage_return
    cmp cl, 0xa  ;Line feed's ASCLL is 0xa
    jz .is_line_feed
    cmp cl, 0x8  ;backspace's ASCLL is 0x8
    jz .is_backspace
    jmp .put_other

.is_backspace:
    dec bx     ;bx - 1,and the bx is cursor's value.
    shl bx, 1  ;make the cursor's value to real address in video memory.

    mov byte [gs:bx], 0x20  ;0x20:the ASCLL of space.
    inc bx     ;bx + 1  ;bx(real address) is point to the char's attribute
    mov byte [gs:bx], 0x07  ;black background and white font color.
    shr bx, 1  ;make the real address in video memory to cursor's value,and forget the remainder.
    jmp .set_cursor
.put_other:
    shl bx, 1
    mov byte [gs:bx], cl
    inc bx
    mov byte [gs:bx], 0x07
    shr bx, 1
    inc bx
    cmp bx, 2000  ;if cursor's val < 2000,it indicate that the address is not the end of video memory.
    jl .set_cursor
.is_line_feed:
.is_carriage_return:
    xor dx, dx
    mov ax, bx
    mov si, 80

    div si

    sub bx, dx
.is_carriage_return_end:
    add bx, 80
    cmp bx, 2000
.is_line_feed_end:
    jl .set_cursor
;_____________Pay attention : this code is not achieving Scroll!________________________________________
.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  ;is the otherside of pushad
    ret    ;return from put_char function.

;--------------put_str----------
global put_str
put_str:
    push ebx
    push ecx
    xor ecx, ecx
    mov ebx, [esp + 12] ;why is 12?:one return address,two register.
.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_int-----------------
;Payattention!!
;The bochs's memory is Little Endian.
;we want to print char in screen by Big Endian(human's habit)like 15,0xfc0 and so on.
;so, we need two step;
;First step: make each int number to it's ASCLL.
;Second step: print each int number's ASCLL.
global put_int
put_int:
    pushad
    mov ebp, esp
    mov eax, [ebp+4*9] ;get the int number to eax,and 4*9 is means that one call's return address and eight register.
    mov edx, eax
    mov edi, 7
    mov ecx, 8
    mov ebx, put_int_buffer ;ebx is pointing to buffer address.
.16based_4bits:
    and edx, 0x0000000F ;only remian the int number's low 8bit.
    cmp edx, 9
    jg .is_A2F  ;"A2F" is "A to F".
    add edx, '0'  ;Make int number to ASCLL
    jmp .store
.is_A2F:
    sub edx, 10
    add edx, 'A'  ;Make int number to ASCLL(Hex)
.store:
    mov [ebx+edi], dl
    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
    dec edi
    jmp .put_each_num
.full0:
    mov cl, '0'
;the program in below(on .full0) is created by myself,because i think, when all int number is '0',and at that time,the edi also is 8,we can not use this commend `mov cl,[put_int_buffer+edi]`,the cl register store some data that we don't know,it is so dangerous.
    push ecx
    call put_char
    add esp, 4
    popad
    ret
.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

C主函数

#include "print.h"

void main(void) {
	put_str("I am kernel\n");
	put_int(0);
	put_char('\n');
	put_int(9);
	put_char('\n');
	put_int(0x00021a3f);
	put_char('\n');
	put_int(0x00000000);
	while(1);
}

本文作者:宇星海

本文链接:https://www.cnblogs.com/Yu-Xing-Hai/p/18621123/print_S

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   宇星海  阅读(18)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起