光标的位置
CRT Controller Data Registers
中索引为 0Eh 的 Cursor Location High Register 寄存器和索引为 0Fh 的 Cursor Location Low Register 寄存器,这两个寄存器都是 8 位长度,分别用来存储光标坐标的低 8 位和高 8 位地址。访问 CRT controller 寄存器组的寄存器,需要先往端口地址为 0x3D4 的 Address Register 寄存器中写入寄存器的索引,再从端口地址为 0x3D5 的 Data Register 寄存器读、写数据。
获得的光标
注意:我们获得的光标的位置是0~1999,表示2000个位置,一个位置表示一个字符,我们要设置字符之前要乘2,然后就可以设置要显示的字符,和字符的属性,低字节显示的是字符,高字节是要显示字符的属性。
global put_char
put_char:
pushad
mov ax,SELECTOR_VIDEO
mov gs,ax
mov dx,0x3d4
mov al,0x0e
out dx,al ;往0x3d4的address register寄存器中写入寄存器的索引
mov dx,0x03d5
in al,dx ; 在从端口地址0x3d5的data register寄存器中读写数据
mov ah,al ; 我们获取了坐标的高8位
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x03d5
in al,dx
mov bx,ax ;bx成为了下一个待打印字符的光标作标值
mov ecx, [esp+36] ; 获取需要打印的字符
cmp cl,0xd ;CR是0x0d,LF是0x0a
jz .is_carriage_return ;回车
cmp cl,0xa
jz .is_line_feed ;换行
cmp cl,0x8 ;退格
jz .is_backspace
jmp .put_other
.is_backspace:
dec bx
shl bx,1 ;相当于乘以2
mov byte [gs:bx],0x20 ;将待删除的字节补为0或空格
inc bx
mov byte [gs:bx],0x07
shr bx,1 ;除以2
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 ;如果不小于2000,就需要滚屏幕
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
.roll_screen:
cld
mov ecx,960
mov esi,0xc00b80a0
mov edi,0xc00b8000
mov ebx,3840
mov ecx,80
.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,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
mov al,bl
out dx,al
.put_char_done:
popad
ret