Bootstrap

UEFI开发探索51 – UEFI下的打印函数

(请保留-> 作者: 罗冰   https://blog.csdn.net/luobing4365)

原计划的50篇博客,终于完成了。可惜的是,现有的篇幅无法把最初设想的内容全部覆盖。因此,开发探索系列还会继续写下去,既然原定目标50篇已经达成,新的目标篇数就不设定了。只针对我感兴趣的各方面的内容,继续探索。

在日常的开发中,总会用到各种打印(print)函数。UEFI下的打印函数,其格式有点奇怪,和Windows及Linux下的print函数不大相同。而且又涉及到Ascii字符和Unicode字符的支持,在使用中总是犯一些小错,让人很是恼火。

因此,为了方便后面的开发,我准备将相关的打印函数整理出来,以备参考。

1OutputString()

最基本的打印输出函数,其他Print函数都是基于此函数构建的。其函数原型为:

typedef
EFI_STATUS
(EFIAPI *EFI_TEXT_STRING) (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 
*This,//指向    
                                 //EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
实例的指针
IN CHAR16 *String //Null结尾的字符串
);

此函数向输出设备写入字符串,显示在当前光标处,是最基本的输出机制。

示例如下:

gST->ConOut->OutputString(gST->ConOut,L”Hello, UEFI World!\n\r”);

这是Simple Text Output Protocol最核心的函数,配合此Protocol的其他函数,比如SetAttribute等,可以实现彩色背景和彩色字体。在之前的博客中曾经展示过,可以做出与众不同的命令行式Sell程序。

格式化输出

PrintLib提供了格式化输出的支持函数,它支持所有Unicode和ASCII字符串。在Package的dsc文件中,一般都会提供这个库的编译。如下:

PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf

UEFI的格式化方式,与ANSI C的标准不尽相同,在使用的时候,很容易造成概念混淆。

格式化语法如下:

%[flags][width][.precision]type

[flags]

   ● –     左对齐标志,如果没有设置,则为右对齐
   ● 空格  对数字类型字符添加前置空格,对类型X,x和d有效
   ● +     符号前缀,显示数字的正负,对类型X,x和d有效,与空格同用时,忽略空格
   ● 0     以先导0补充数字左侧,常配合后续的width(宽度)使用,对类型X,x和d
有效;
   ● ,     以千位分隔符表示数字,只对类型d有效,与标志0同用时,忽略0;
   ● L,l   将指定的数字以UINT64型打印,对类型X,x和d有效。如果不指定此标志,
则以int型打印;
   注意:非上述标志都会被忽略掉

[width]

   ● *     由参数列表中的数字给出宽度。如Print(L”%0*d”,5,a)表示以宽度5表示变量
a, 不足由前导0补上;
   ● 数字  由此10进制数给出需要表示的宽度;
   注意:如果width的值没有给出,缺省以0指定。

[.precision]

   ● *     由参数列表中的数字给出宽度;
   ● 数字  由此10进制数给出需要表示的精度;
   注意:如果此域没有给出,缺省以0指定。

type

   ● %     打印个百分号出来
   ● c     打印Unicode字符。ASCII字符也可以使用此类型,只要保证其bits8…15为0;
   ● x     将需要打印的参数认为是无符号十进制数,以十六进制形式打印参数。与ANSI C标准不同;
   ● X     将需要打印的参数认为是无符号十进制数,以十六进制形式打印参数,同时以前导0填充。与ANSI C标准不同;
   ● d     将需要打印的参数认为是有符号十进制数,以十进制形式打印参数;
   ● p     需要打印的参数是指针(Void *),以无符号十六进制形式打印出指针地址;
   ● a     参数为指向ASCII字符串的指针,与ANSI C标准不同;
   ● S,s    参数为指向Unicode字符串的指针,与ANSI C标准不同;
   ● g     参数为指向GUID结构的指针,用来打印GUID的。与ANSI标准不同;
   ● t     参数为指向EFI_TIME结构的指针,以mm/dd/yyyy hh:mm的形式打印,不足位的以0填充。与ANSI C标准不同;
   ● r     参数为RETURN_STATUS值,将此值代表的含义转换为字符串,打印出来。与ANSI C标准不同。
     以下为部分转换对照:(具体值的含义可参考EDKII代码中的\BaseTools\Source\C\Include\Common\UefiBaseTypes.h)
            RETURN_SUCCESS:  “Success”
            RETURN_LOAD_ERROR:  “Load Error”
            RETURN_INVALID_PARAMETER:  “Invalid Parameter”
            RETURN_UNSUPPORTED: “Unsupported”

示例说明:

对于flags的’+’,虽然对type(类型)x和X都有效,不过因为x和X都是以无符号来处理参数的,个人觉得没有什么作用。

  INTN a=-234;
  Print(“a=%+d\n”,a);
  Print(“a=0x%+x\n”,a);
输出为:
  a=-234
  a=0xFFFFFF16

比较常用,也容易搞错的类型是’a’、’S’和’s’。含义与ANSI中完全不同,主要用来区分UEFI下ANSI字符串和Unicode字符串。

  CHAR8 * str=”Hello, UEFI World!\n”;
  Print(L”%a”,str);
输出:
  Hello,UEFI World!

在编程时,针对两种编码,在使用时需要注意。比如上述例子,习惯性会使用”%s”去打印,当然是什么都打印不出来的,因为str不是有效的Unicode字符串。

常用打印相关函数

UINTN EFIAPI Print(IN CONST CHAR* Format, …)
参数:
  Format:以Null结尾的Unicode字符串
  …:按格式化规则给定的变量参数列表
返回:
    按格式输出变量到ConOut
函数说明:
  按照Format中给出的格式化字符串,通过ConOut输出变量。输出变量的大小如果大于PcdUefiLibMaxPrintBufferSize,则按照此长度截断,输出到ConOut。

UINTN EFIAPI PrintXY  ( IN UINTN  PointX, 
  IN UINTN  PointY, 
  IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *  ForeGround, 
  IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *  BackGround, 
  IN CONST CHAR16 *  Format, 
    …  
 )
参数:
  PointX:   显示字符串位置的X坐标
  PointY:   显示字符串位置的Y坐标
  ForeGround: 字符串显示的字体颜色,可设置为NULL,直接使用ConOut设备的
  背景色
  BackGround: 字符串显示的字体背景颜色,可设置为NULL,直接使用ConOut设
  备的背景色
      Format:    以NULL结尾的Unicode格式化字符串。
      …:按格式化规则给定的变量参数列表
返回:
  按格式输出变量
函数说明:
  按照Format中给出的格式化字符串,在图形模式下,通过ConOutputHandle输出变量,输出位置在(PointX,PointY)。如果输出位置超过显示规定的边缘,则显示的图像会被截断。可配合HII来实现显示。

UINTN EFIAPI AsciiPrint  ( IN CONST CHAR8 *  Format,  …  )
参数:
  Format:以Null结尾的ASCII字符串
  …:按格式化规则给定的变量参数列表
返回:
     按格式输出变量到ConOut
函数说明:
  按照Format中给出的格式化字符串,通过ConOut输出变量。输出变量的大小如果大于PcdUefiLibMaxPrintBufferSize,则按照此长度截断,输出到ConOut。

其他函数:

UINTN EFIAPI StrLen  ( IN CONST CHAR16 *  String  ) ;
UINTN EFIAPI AsciiStrLen  ( IN CONST CHAR8 *  String  ) ;
CHAR16* EFIAPI StrCpy  ( OUT CHAR16 *  Destination,   IN CONST CHAR16 *  Source   );
CHAR8* EFIAPI AsciiStrCpy  ( OUT CHAR8 *  Destination,   IN CONST CHAR8 *  Source  );
INTN EFIAPI StrCmp  ( IN CONST CHAR16 *  FirstString,   IN CONST CHAR16 *  SecondString );
INTN EFIAPI AsciiStrCmp  ( IN CONST CHAR8 *  FirstString,   IN CONST CHAR8 *  SecondString );  
CHAR8* EFIAPI UnicodeStrToAsciiStr  ( IN CONST CHAR16 *  Source,   OUT CHAR8 *  Destination   );
CHAR16* EFIAPI AsciiStrToUnicodeStr  ( IN CONST CHAR8 *  Source,   OUT CHAR16 *  Destination  );

;