一、print.S文件说明
- put_char 函数(每次只打印一个字符)是各种打印函数的核心
1.1 功能说明
- put_char 函数的处理流程
- 备份寄存器现场;
- 获取光标坐标值,光标坐标值是下一个可打印字符的位置;
- 为了在光标处打印字符,需要读取光标坐标寄存器,获取光标坐标值。
- 获取待打印的字符;
- 判断字符是否为控制字符:
- 若是回车符、换行符、退格符三种控制字符之一,则进入相应的处理流程;
- 否则,其余字符都被粗暴地认为是可见字符,进入输出流程处理;
- 判断是否需要滚屏;
- 更新光标坐标值,使其指向下一个打印字符的位置;
- 恢复寄存器现场,退出;
1.2 原理说明
- put_char 的打印原理是直接写显存;
- 在 32 位保护模式下对内存的操作是“段基址(选择子):段内偏移量”,因此需要使用到视频段选择子,我们使用段寄存器 gs 来存储视频段选择子。
二、前置知识点
- 显卡操作只用到了CRT Controller Registers 分组中的寄存器;
- CRT controller 寄存器组的 Address Register 的端口地址默认为0x03D4,Data Register 的端口地址0x03D5。
- 计算机工程师把数据结构中数组的知识用到了硬件中。他们把每一个寄存器分组视为一个寄存器数组,提供一个寄存器用于指定数组下标,再提供一个寄存器用于对索引所指向的数组元素(也就是寄存器)进行输入输出操作。这样用这两个寄存器就能够定位寄存器数组中的任何寄存器。
- 这两个寄存器就是各组中的 Address Register 和 Data Register。
- Address Register 作为数组的索引(下标);
- Data Register 作为寄存器数组中该索引对应的寄存器,它相当于所对应的寄存器的窗口,往此窗口读写的数据都作用在索引所对应的寄存器上;
- 对这类分组的寄存器操作方法是先在 Address Register 中指定寄存器的索引值,用来确定所操作的寄存器是哪个,然后在Data Register 寄存器中对所索引的寄存器进行读写操作;
三、代码展示
汇编代码
TI_GDT equ 0
RPL0 equ 0
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ;create SELECTOR_VIDEO.
[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
.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.
C主函数
#include "print.h"
void main(void) {
put_char('k');
put_char('e');
put_char('r');
put_char('n');
put_char('e');
put_char('l');
put_char('\n');
put_char('1');
put_char('2');
put_char('\b');
put_char('3');
while(1);
}
标签:字符,打印函数,1.2,Register,char,put,寄存器,print,光标
From: https://www.cnblogs.com/Yu-Xing-Hai/p/18621123/print_S