Step to UEFI (290)Cpp UEFI 006 抄一个 Print

前面编写测试代码的过程中,总感觉没有 Print 直接输出来的顺手,于是研究了一下 Print 的实现。基本原理是,对变量格式化后输出到一个 字符串Buffer 中,然后直接输出Buffer。

首先,编写一个测试的 CPP:

#include <UEFI/UEFI.h>
#include <type_traits>
#include "print.h"

EFI_SYSTEM_TABLE* gST;

EFI_STATUS
efi_main(EFI_HANDLE /*image*/, EFI_SYSTEM_TABLE* systemTable)
{
	gST=systemTable;
	Print(u"%d\n",2024);
	return EFI_SUCCESS;
}

其中使用了 Print.h 头文件,定义如下:

UINTN
EFIAPI
Print (
  IN const CHAR16 *Format,
  ...
);

接下来编写Print.cpp,关键代码来自\MdePkg\Library\UefiLib\UefiLibPrint.c

UINTN
EFIAPI
Print (
  IN CONST CHAR16  *Format,
  ...
  )
{
  VA_LIST  Marker;
  UINTN    Return;

  VA_START (Marker, Format);

  Return = InternalPrint (Format, gST->ConOut, Marker);

  VA_END (Marker);

  return Return;
}

其中的InternalPrint() 函数有较大改动,直接在函数中开了一个内存用于当作 Buffer (CharBuffer[]),不需要AllocatePool()动态分配。

UINTN
InternalPrint (
  IN  CONST CHAR16                     *Format,
  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *Console,
  IN  VA_LIST                          Marker
  )
{
  EFI_STATUS  Status;
  UINTN       Return;
  CHAR16      *Buffer;
  UINTN       BufferSize;
  CHAR16      CharBuffer[320];

  ASSERT (Format != NULL);
  ASSERT (((UINTN)Format & BIT0) == 0);
  ASSERT (Console != NULL);

  BufferSize = 320;

  Buffer = &CharBuffer[0];
  ASSERT (Buffer != NULL);

  Return = UnicodeVSPrint (Buffer, BufferSize, Format, Marker);

  if ((Console != NULL) && (Return > 0)) {
    //
    // To be extra safe make sure Console has been initialized
    //
    Status = Console->OutputString (Console, Buffer);
  }

  return Return;
}

接下来编写编译的批处理,可以看到最主要是编译生成 test8.obj 和 print.obj ,最后将二者Link 在一起即可:

set Target=test8
cl /c /I"C:\\BuildBs\\CppStudy\\Cpp\\UEFI-CPP-headers" /Zc:wchar_t- /Zi /W4 /WX- /diagnostics:column /Od /D _UNICODE /D UNICODE /D HAVE_USE_MS_ABI /D GNU_EFI_USE_EXTERNAL_STDARG /D _UNICODE /D UNICODE /Gm- /MDd /GS- /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++17 /Fo"C:\\BuildBs\\CppStudy\\Cpp\\" /FAsc /Fd"C:\\BuildBs\\CppStudy\\Cpp\\vc142.pdb" /external:W4 /Gd /TP /wd4229 /FC /errorReport:prompt /Oi- %Target%.cpp
cl /c /I"C:\\BuildBs\\CppStudy\\Cpp\\UEFI-CPP-headers" /Zc:wchar_t- /Zi /W4 /WX- /diagnostics:column /Od /D _UNICODE /D UNICODE /D HAVE_USE_MS_ABI /D GNU_EFI_USE_EXTERNAL_STDARG /D _UNICODE /D UNICODE /Gm- /MDd /GS- /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++17 /Fo"C:\\BuildBs\\CppStudy\\Cpp\\" /FAsc /Fd"C:\\BuildBs\\CppStudy\\Cpp\\vc142.pdb" /external:W4 /Gd /TP /wd4229 /FC /errorReport:prompt /Oi- print.cpp

if %errorlevel% NEQ 0 goto EndError

link "/OUT:C:\\BuildBs\\CppStudy\\Cpp\\%Target%.efi" /VERBOSE /INCREMENTAL:NO "/LIBPATH:C:\\BuildBs\\CppStudy\\Cpp\\" libcmtd.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NODEFAULTLIB /MANIFEST:NO /DEBUG:FULL "/PDB:C:\\BuildBs\\CppStudy\\Cpp\\bootx64.pdb" /SUBSYSTEM:EFI_APPLICATION /OPT:REF /TLBID:1 "/ENTRY:efi_main" /NXCOMPAT:NO "/IMPLIB:C:\\BuildBs\\CppStudy\\Cpp\\bootx64.lib" /MACHINE:X64 "C:\\BuildBs\\CppStudy\\Cpp\\%Target%.obj" "C:\\BuildBs\\CppStudy\\Cpp\\print.obj"
copy /y %Target%.efi Emulator\
:EndError

最后将Print.cpp、Test8.CPP和g8.bat 放在一起,即可编译。

模拟器运行结果如下:

完整代码如下,需要注意的是编译批处理内部使用了绝对路径,如果想实验,最好按照之前的文章架设同样名称的目录测试。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注