前面编写测试代码的过程中,总感觉没有 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 放在一起,即可编译。
模拟器运行结果如下:
完整代码如下,需要注意的是编译批处理内部使用了绝对路径,如果想实验,最好按照之前的文章架设同样名称的目录测试。