实现字符打印(实验)

文章展示了如何使用x86汇编语言实现`stdint.h`中的数据类型,并定义了`put_char`函数来打印字符,包括处理回车、换行和退格的情况。此外,还给出了`put_str`函数的雏形,用于打印字符串。在`main.c`中,这些功能被用于输出特定字符串。
摘要由CSDN通过智能技术生成

stdint.h

#ifndef __LIB_STDINT_H
#define __LIB_STDINT_H
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed long long int int64_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long int uint64_t;
#endif

print.h

#ifndef __LIB_KERNEL_PRINT_H
#define __LIB_KERNEL_PRINT_H
#include "stdint.h"

void put_char(uint8_t char_asci);
#endif

print.S

在这里插入图片描述

RPL0    equ 00b
TI_GDT  equ 000b
SELECTOR_VIDEO       equ (0X0003<<3) + TI_GDT + RPL0 
SELECTOR_DATA	  equ (0X0002<<3) + TI_GDT + RPL0


[bits 32]
section .text
global put_char
;------------------- put_char 函数实现 -------------------------------------------
;把字符写到光标位置
;---------------------------------------------------------------------------------

put_char:

    pushad           		;push all double寄存器
    mov ax,SELECTOR_VIDEO
    mov gs,ax                  ;gs寄存器赋值段选择子
    
    mov dx,0x3D4               ;默认CRT 寄存器索引
    mov al,0xE                 ;这里用al 不用ax 是因为 此处索引寄存器是一个8字节的寄存器
    out dx,al                  ;看光标高位值寄存器 为什么用al 可以推算一下 光标0~1999 2^(8+8)字节完全够用
    mov dx,0x3D5               ;光标高位值寄存器窗口
    in  al,dx                  ;移入ax 
    shl ax,0x8                 ;左移动8位 移动向ah部分 光标位置为8字节
    
    mov dx,0x3D4
    mov al,0xF
    out dx,al
    mov dx,0x3D5
    in  al,dx
    
    mov bx,ax                  ;光标位置转移给bx  
    mov byte cl,[esp+36]       ;4字节返回 只需要把push eip 4字节 + pushad 8*4 32字节算进去即可
                               
                               ;回车0xd 换行0xa 退格0x8 
    cmp cl,0xd                 ;比较cl与0xd是否相等
    je .is_carriage_return     ;回车处理函数
    cmp cl,0xa                 ;比较cl与0xa是否相等 
    je .is_line_feed           ;换行处理函数
    cmp cl,0x8                 ;比较cl与0x8是否相等
    je .is_backspace           ;退格处理函数
    
    jmp .put_other_char
       
 .is_backspace:
    cmp bx,0                   ;如果bx = 0 则没有办法退格了 这是我除书上之外额外加的条件
    je  .set_cursor              
    
    dec bx                     ;光标位置退1
    shl bx,1                   ;一个字符占两个字节 一个字节是属性 一个字节ascii 
    
    mov word [gs:bx],0x0720    ;低字节ascii 32 ascii表示空字符 7 高字符属性空
    shr bx,1                   ;退回原来的位置
    jmp .set_cursor            ;交给硬件去处理光标位置 到时候在那几个老端口把字符信息还回去就完事了
    
 .put_other_char:
    shl bx,1                   
    mov [gs:bx],cl             
    inc bx                     
    mov byte [gs:bx],0x7
    inc bx
    shr bx,1
    cmp bx,2000                ;没到边界2000即跳转
    jl  .set_cursor            ;没有的话 继续往下进行 都2000了其实下面的操作也是不能跳转走的
                               ;只能等下面继续处理滚动屏幕了
                               
    
 .is_carriage_return:
 .is_line_feed:
    xor dx,dx
    mov ax,bx                  ;32位除以16位 被除数高位dx 被除数低位ax div之后余数放在dx 商放在ax
    mov si,80
    div si
    sub bx,dx
    
 .is_carrige_return_end:
    add bx,80                  ;一共一页2000252000/25=80 则向bx增加80
    cmp bx,2000
    jl .set_cursor             
    
 .roll_screen:
    cld                        ;从低到高移动
    mov ax,SELECTOR_DATA 	;我不放心 就初始化了一下
    mov es,ax      
    mov di,es                  
    mov ecx,920
    
    mov esi,0xc00b80a0          ;忘了的这里写一下 ds:si -> es:di (s->d) 源地址si 目的地址di  
    mov edi,0xc00b8000
    rep movsd                  ;movs doubleword 双子
    
    mov ebx,3840               ;最后一行80*2 = 160 4000-160 = 3840 最后一行清除了
    mov ecx,80
    mov esi,0                  
    
 .clean_last_row:   
    mov word [ebx+esi*2],0x720 ;0x07 0x20 属性 空字符
    inc esi     
    loop .clean_last_row
    
    mov ebx,1920
    
 .set_cursor:
    mov dx,0x3D4
    mov al,0xE
    out dx,al
    mov dx,0X3D5
    mov al,bh
    out dx,al
    
    mov dx,0x3D4
    mov al,0XF
    out dx,al
    mov dx,0x3D5
    mov al,bl
    out dx,al
    
    popad                        ;把之前全部储存的给pop出来 还原现场
    ret

mian.c

#include "print.h"
int main()
{	
  put_char('k');
  put_char('e');
  put_char('r');
  put_char('n');
  put_char('e');
  put_char('l');
  put_char('!');
  put_char('\n');
  put_char('i');
  put_char('m');
  put_char(' ');
  put_char('c');
  put_char('o');
  put_char('m');
  put_char('1'); //这里的1是看退格是否生效 
                 //输出时只要是kernel! im coming!即可
  put_char('\b');
  put_char('i');
  put_char('n');
  put_char('g');
  put_char('!');
  while(1);
  return 0;
}

实验

sudo nasm -f elf -o  print.o print.S
sudo gcc -m32 -I kernel/ -c -o kernel/main.o kernel/main.c


sudo ld -m elf_i386  -Ttext 0xc0001500 -e main -o kernel/kernel.bin kernel/main.o kernel/print.o


sudo dd if=/usr/local/bin/kernel/kernel.bin of=/usr/local/bin/hd60M.img bs=512 count=200 seek=9 conv=notrunc

实现字符串打印

print.S

;先自己尝试写一下 
global put_str   
put_str:
    push eax
    push ebx
    push ebp
    xor  eax,eax
    mov  ebp,esp                 ;一般用ebp来寻值 esp上面有个4字节的返回地址 就没有了
    mov  ebx,[ebp+16]            ;指针4字节+12字节寄存器
    
 .put_char_loop:    
    mov byte al,[ebx]
    cmp  al,0
    je  .str_end
    push ax
    call put_char
    add  esp,2
    inc  ebx
    jmp .put_char_loop
    
 .str_end:
    pop ebp
    pop ebx
    pop eax
    ret

main.c

#include "print.h"
int main()
{	
   
   put_str("kernel!\nim com1\bing!");
   put_str("\n");
   put_str("");
   put_str("g");
   put_char('g');
   while(1);
   return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值