1.在lib/kernel/print.S 文件中实现put_char()
2.思路
①我们前面在全局描述符表GDT中定义的第三个描述符,就是我们的显存段描述符,我们往显存段中写入内容,便会输出到屏幕上。
②如果接受到的这个字符是回车/换行/删除键,那么需要特殊处理
③如果当前整个屏幕满了,那么需要整体往上挪一行,把最后一行空出来
④默认是80*25的
⑤每次都需获取光标位置,来进行接下来的操作
⑥访问CRT controller寄存器组的的寄存器,先往端口地址为0x03d4的Address Register寄存器中写入寄存器的索引,然后再从端口地址为0x03d5的Data Register寄存器中读写数据
3.代码
TI_GDT EQU 0
RPL0 EQU 0
SELECTOR_VIDEO EQU (0x0003<<3) + TI_GDT + RPL0
//显存段描述符
[BITS 32]
SECTION .text
;/*******put char**********/
global put_char
put_char:
PUSHAD ;save all registers
MOV AX, SELECTOR_VIDEO
MOV GS, AX
;获取当前光标的位置
;High 8 bit
MOV DX, 0x03d4
MOV AL, 0x0e
OUT DX, AL
MOV DX, 0x03d5
IN AL, DX
MOV AH, AL
;Low 8 bit
MOV DX, 0x03d4
MOV AL, 0x0f
OUT DX, AL
MOV DX, 0x03d5
IN AL, DX
MOV BX, AX ;保存当前的光标位置
;获取栈中那个 字符参数
MOV ECX, [ESP+36]
CMP CL, 0xd
JZ .is_carriage_return
CMP CL, 0xa ;换行
JZ .is_line_feed
CMP CL, 0x8 ;退格 也就是删除键
JZ .is_backspace
JMP .put_other
.is_backspace://如果是删除键
DEC BX ;cursor--
SHL BX, 1
MOV byte [GS:BX], 0x20 ;0x20 = space
INC BX
MOV byte [GS:BX], 0x07
SHR BX, 1
JMP .set_cursor
.put_other:
SHL BX, 1
MOV [GS:BX], CL ;CL is Ascii
INC BX
MOV byte [GS:BX], 0x07 ;black background white font
SHR BX, 1
INC BX
CMP BX, 2000 ;line end
JL .set_cursor
.is_line_feed: ;\n \r
.is_carriage_return:
MOV DX, 0
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
.roll_screen: ;start addr 0xc00b8000 all is 2000
CLD ;DF = 0
MOV ECX, 960 ;2000-80 = 1920 1920*2 = 3840 Bytes
;3840/4 = 960
MOV ESI, 0xc00b80a0 ;a0 = 160 = 80*2Bytes
MOV EDI, 0xc00b8000
REP MOVSD
;/*******Clear End Line****/
MOV EBX, 3840
MOV ECX, 80
.cls:
MOV word [GS:EBX], 0x0720 ;black background white font
ADD EBX, 2
LOOP .cls
MOV BX, 1920 ;last line
.set_cursor://写入新的光标位置
;Set High 8 bit
MOV DX, 0x03d4
MOV AL, 0x0e
OUT DX, AL
MOV DX, 0x03d5
MOV AL, BH
OUT DX, AL
;Set Low 8 bit
MOV DX, 0x03d4
MOV AL, 0x0f
OUT DX, AL
MOV DX, 0x03d5
MOV AL, BL
OUT DX, AL
.put_char_down:
popad
ret