|
|
@@ -0,0 +1,135 @@
|
|
|
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
+; 打印函数
|
|
|
+; 1. 备寄存器现场
|
|
|
+; 2. 获取光标坐标值,光标坐标值是下一个可打印字符的位置
|
|
|
+; 3. 获取打印的字符
|
|
|
+; 4. 判断字符是否为控制字符,若是回车符、换行符、退格符三种控制字符之一,则进入相应的处理流程。
|
|
|
+; 否则,其余字符都粗暴地认为是可见字符,进入输出流程处理
|
|
|
+; 5. 判断是否需要滚屏
|
|
|
+; 6. 更新光标坐标值,便其指向下一个打印字符的位置
|
|
|
+; 7. 恢复寄存器现场,退出
|
|
|
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
+
|
|
|
+TI_GDT equ 0
|
|
|
+RPL0 equ 0
|
|
|
+SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0
|
|
|
+CR equ 0xd
|
|
|
+LF equ 0xa
|
|
|
+BACKSPACE equ 0x8
|
|
|
+
|
|
|
+[bits 32]
|
|
|
+section .text
|
|
|
+global put_char ; 导出为全局符号
|
|
|
+;------------------------ put_char -----------------------------------------------
|
|
|
+; 功能描述: 把栈中的 1 个字符写入光标所在处
|
|
|
+;---------------------------------------------------------------------------------
|
|
|
+put_char:
|
|
|
+ pushad ; 备份 32 位寄存器环境 8 个 eax->ecx->edx->ebx->esp->ebp->esi->edi
|
|
|
+ mov ax, SELECTOR_VIDEO
|
|
|
+ mov gs, ax ; gs 为正确的视频段选择子
|
|
|
+
|
|
|
+;;;;;;;;;;;; 获取当前光标位置 ;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
+ ; 先获取高 8 位
|
|
|
+ mov dx, 0x3d4 ; 索引寄存器
|
|
|
+ mov al, 0x0e ; 用于提供光标位置的高 8 位
|
|
|
+ out dx, al
|
|
|
+ mov dx, 0x3d5 ; 通过读写数据端口 0x3d5 来获取或设置光标位置
|
|
|
+ in al, dx ; 得到了光标位置的高 8 位
|
|
|
+ mov ah, al
|
|
|
+ ; 再获取低 8 位
|
|
|
+ mov dx, 0x3d4
|
|
|
+ mov al, 0x0f
|
|
|
+ out dx, al
|
|
|
+ mov dx, 0x3d5
|
|
|
+ in al, dx
|
|
|
+
|
|
|
+ ; 将光标存入 bx
|
|
|
+ mov bx, ax
|
|
|
+ mov ecx, [esp + 36] ; pushad 压入 4 X 8 = 32 字节 加上主调函数 4 字节
|
|
|
+
|
|
|
+ cmp cl, CR ; 0x0d
|
|
|
+ jz .is_carriage_return
|
|
|
+ cmp cl, LF ; 0x0a
|
|
|
+ jz .is_line_feed
|
|
|
+ cmp cl, BACKSPACE ; BS(backspace) 的 asc 码是 0x8
|
|
|
+ jz .is_backspace
|
|
|
+ jmp .put_other
|
|
|
+;;;;;;;;;;;;
|
|
|
+.is_backspace:
|
|
|
+;;;;;;;;;;;; backspace 的一点说明 ;;;;;;;;;;;;;;;;;
|
|
|
+; 当为 backspace 时,本质上只要将光标向前一个显存位置即可。后面输入的字符自然会覆盖此处
|
|
|
+; 的字符,但有可能在键入 backspace 后并不再输入新的字符,这时光标已经向前移动到代删除的
|
|
|
+; 字符位置,但字符还在原处。所以此处添加了空格或空字符 0
|
|
|
+ dec bx ; 光标指向前一个字符
|
|
|
+ shl bx, 1 ; 光标值乘 2 表示光标对应显存中的偏移字节
|
|
|
+ mov byte [gs:bx], 0x20 ; 将待删除的字节补为 0 或空格(0x20)皆可
|
|
|
+ inc bx
|
|
|
+ mov byte [gs:bx], 0x07
|
|
|
+ shr bx, 1
|
|
|
+ jmp .set_cursor
|
|
|
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
+
|
|
|
+.put_other:
|
|
|
+ shl bx, 1 ; 光标位置用 2 字节表示,将光标值乘 2 表示对应显存中的偏移字节
|
|
|
+
|
|
|
+ mov [gs:bx], cl ; ASCII 字符本身
|
|
|
+ inc bx
|
|
|
+ mov byte [gs:bx], 0x07 ; 字符属性(黑屏白字)
|
|
|
+ shr bx, 1 ; 恢复老的光标值
|
|
|
+ inc bx ; 下一个光标值
|
|
|
+ cmp bx, 2000
|
|
|
+ jl .set_cursor ; 若光标值小于 2000,表示未写到显存的最后,则去设置新的光标值
|
|
|
+ ; 若超出屏幕字符数大小(2000),则换行处理
|
|
|
+.is_line_feed: ; 换行符 LF (\n)
|
|
|
+.is_carriage_return: ; 回车符 CR (\r)
|
|
|
+ ; 如果是 CR (\r),只要把光标移动到行首就行
|
|
|
+ xor dx, dx ; dx 是被除数的高 16 位,清 0
|
|
|
+ mov ax, bx ; ax 是被除数的低 16 位
|
|
|
+ mov si, 80
|
|
|
+ ; 由于是效仿 Linux,Linux 系统中 \n 表示下一行的行首,所以本系统中也把\n 和 \r 都处理
|
|
|
+ ; 成 Linux 中的 \n 的意思,也就是下一行的行首
|
|
|
+ div si
|
|
|
+
|
|
|
+ sub bx, dx
|
|
|
+.is_carriage_return_end: ; 回车符 CR 处理结束
|
|
|
+ add bx, 80
|
|
|
+ cmp bx, 2000
|
|
|
+.is_line_feed_end: ;若是 LF(\n),将光标移+80
|
|
|
+ jl .set_cursor
|
|
|
+
|
|
|
+ ; 屏幕行范围是 0~24,滚屏的原理是将屏幕的第 1~24 行搬运到 0~23 行,再将第 24 行用空格填充
|
|
|
+.roll_screen: ; 若超出屏幕大小,开始滚屏
|
|
|
+ cld ; DF = 0
|
|
|
+ mov ecx, 960 ; 2000-80=1920 个字符要搬运,共 1920*2=3840 字节,一次搬运 4 字节,共 960 次
|
|
|
+ mov edi, 0xc00b8000 ; 第 0 行行首
|
|
|
+ mov esi, 0xc00b80a0 ; 第 1 行行首 0xc00b8000 + (80 * 2)
|
|
|
+ rep movsb
|
|
|
+
|
|
|
+ ; 将最后一行填充为空白
|
|
|
+ mov ebx, 3840 ; 最后一行首字符的第一个字节偏移=1920*2
|
|
|
+ mov ecx, 80 ; 一行 80 字符(160 字节),每次清空 1 字符,需要移动 80 次
|
|
|
+.cls:
|
|
|
+ mov word [gs:ebx], 0x0720 ; 0x07 黑底白字 0x20 空格符
|
|
|
+ add ebx, 2
|
|
|
+ loop .cls
|
|
|
+ mov bx, 1920 ;!将光标值重置为 1920,最后一行的首字符
|
|
|
+
|
|
|
+.set_cursor:
|
|
|
+ ; 将光标设为 bx 值
|
|
|
+ ;; 1. 先设置高 8 位
|
|
|
+ mov dx, 0x3d4 ; 索引寄存器
|
|
|
+ mov al, 0x0e ; 用于提供光标位置的高 8 位
|
|
|
+ out dx, al
|
|
|
+ mov dx, 0x3d5 ; 通过读写数据端口 0x3d5 来获取或设置光标位置
|
|
|
+ mov al, bh
|
|
|
+ out dx, al
|
|
|
+ ;; 2. 再设置低 8 位
|
|
|
+ mov dx, 0x3d4
|
|
|
+ mov al, 0x0f
|
|
|
+ out dx, al
|
|
|
+ mov dx, 0x3d5
|
|
|
+ mov al, bl
|
|
|
+ out dx, al
|
|
|
+.put_char_done:
|
|
|
+ popad
|
|
|
+ ret
|