Step to UEFI (12) —- EADK 中的 Time 函数

这次的工作是基于 EADK 的,编译借用EADK的环境这样做有编译速度快,测试方便的优点,具体配置请参考 ”http://www.lab-z.com/how-to-use-eadk/“

关于 time_t 的定义可以在 \EadkPkg_A2\StdLib\Include\time.h 中看到。测试了一下,sizeof(time_t) == 4。

time()函数取得的结果是当前系统的时间,具体的定义是:返回从GMT1970年1月1日 0:0:0 开始经过的秒数表示的当前时间和日期。

下面可以看到 time() 有两种用法。

代码如下,很简单

/** @file
    A simple, basic, application showing how the Hello application could be
    built using the "Standard C Libraries" from StdLib.

    Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
    This program and the accompanying materials
    are licensed and made available under the terms and conditions of the BSD License
    which accompanies this distribution. The full text of the license may be found at
    http://opensource.org/licenses/bsd-license.

    THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
    WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
//#include  <Uefi.h>
//#include  <Library/UefiLib.h>
//#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <time.h>

/***
  Demonstrates basic workings of the main() function by displaying a
  welcoming message.

  Note that the UEFI command line is composed of 16-bit UCS2 wide characters.
  The easiest way to access the command line parameters is to cast Argv as:
      wchar_t **wArgv = (wchar_t **)Argv;

  @param[in]  Argc    Number of argument tokens pointed to by Argv.
  @param[in]  Argv    Array of Argc pointers to command line tokens.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
int
EFIAPI
main (
  IN int Argc,
  IN char **Argv
  )
{

  time_t t;

  time(&t);

  printf("%ld\n",t);

  t=time(NULL);

  printf("%s\n",ctime(&t));

  return 0;
}

 

运行结果

time

下载

main

参考:

1.http://ganquan.info/standard-c/function/time Standard C 语言标准函数库速查 (Cheat Sheet)

2.书籍 《C函数实用手册》 个人感觉这本书不错,蛮实用,实例详尽。缺点是缺少一些新的C函数,可能是因为这本书目标是TC,并且是2003年的书籍。当初是为了用 Turbo C 所以才买的.

Step to UEFI (11)—- 让程序中断运行的方法

前面介绍了 Pasue 一下的方法,这里再介绍一下让程序停止执行的方法。 和 DOS 不同,Shell中的程序通常都像吃过炫迈口香糖那样—-根本停不下来…..要想能够中止运行,可以通过 Shell Environment 2 特别设置一下。

下面的代码设置了 ESC 为Break 键,运行中可以使用它来终止运行,这里的代码和前面 PauseTest的长得几乎一样:

//
// PauseTest.c
//
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/BaseMemoryLib.h>

#define SHELL_INTERFACE_PROTOCOL \
  { \
    0x47c7b223, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellInterfaceProtocol  = SHELL_INTERFACE_PROTOCOL;
EFI_GUID		SEGuid = EFI_SE_EXT_SIGNATURE_GUID;
//
// The shell environment is provided by a driver.  The shell links to the
// shell environment for services.  In addition, other drivers may connect
// to the shell environment and add new internal command handlers, or
// internal protocol handlers.
//
#define SHELL_ENVIRONMENT_INTERFACE_PROTOCOL \
  { \
    0x47c7b221, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellEnvProtocol = SHELL_ENVIRONMENT_INTERFACE_PROTOCOL;

#define EFI_OUTPUT_PAUSE    0x00000002

typedef struct {
  SHELLENV_EXECUTE          Execute;  // Execute a command line
  SHELLENV_GET_ENV          GetEnv;   // Get an environment variable
  SHELLENV_GET_MAP          GetMap;   // Get mapping tables
  SHELLENV_ADD_CMD          AddCmd;   // Add an internal command handler
  SHELLENV_ADD_PROT         AddProt;  // Add protocol info handler
  SHELLENV_GET_PROT         GetProt;  // Get the protocol ID
  SHELLENV_CUR_DIR          CurDir;
  SHELLENV_FILE_META_ARG    FileMetaArg;
  SHELLENV_FREE_FILE_LIST   FreeFileList;

  //
  // The following services are only used by the shell itself
  //
  SHELLENV_NEW_SHELL        NewShell;
  SHELLENV_BATCH_IS_ACTIVE  BatchIsActive;

  SHELLENV_FREE_RESOURCES   FreeResources;
} EFI_SHELL_ENVIRONMENT;

EFI_SHELL_INTERFACE             *SI;
EFI_SHELL_ENVIRONMENT           *SE;
EFI_SHELL_ENVIRONMENT2          *SE2;

EFI_BOOT_SERVICES     *gBS;
EFI_RUNTIME_SERVICES  *gRT;
EFI_SYSTEM_TABLE      *gST;

//Copy from \Shell\Library\Misc.c
BOOLEAN
GrowBuffer (
  IN OUT EFI_STATUS   *Status,
  IN OUT VOID         **Buffer,
  IN UINTN            BufferSize
  )
/*++

Routine Description:

  Helper function called as part of the code needed
  to allocate the proper sized buffer for various 
  EFI interfaces.

Arguments:

  Status      - Current status

  Buffer      - Current allocated buffer, or NULL

  BufferSize  - Current buffer size needed
    
Returns:
    
  TRUE - if the buffer was reallocated and the caller 
  should try the API again.

--*/
{
  BOOLEAN TryAgain;

  //
  // If this is an initial request, buffer will be null with a new buffer size
  //
  if (NULL == *Buffer && BufferSize) {
    *Status = EFI_BUFFER_TOO_SMALL;
  }
  //
  // If the status code is "buffer too small", resize the buffer
  //
  TryAgain = FALSE;
  if (*Status == EFI_BUFFER_TOO_SMALL) {

    if (*Buffer) {
      FreePool (*Buffer);
    }

    *Buffer = AllocateZeroPool (BufferSize);

    if (*Buffer) {
      TryAgain = TRUE;
    } else {
      *Status = EFI_OUT_OF_RESOURCES;
    }
  }
  //
  // If there's an error, free the buffer
  //
  if (!TryAgain && EFI_ERROR (*Status) && *Buffer) {
    FreePool (*Buffer);
    *Buffer = NULL;
  }

  return TryAgain;
}

//Copy from \Shell\Library\Handle.c
EFI_STATUS
LocateHandle (
  IN EFI_LOCATE_SEARCH_TYPE       SearchType,
  IN EFI_GUID                     * Protocol OPTIONAL,
  IN VOID                         *SearchKey OPTIONAL,
  IN OUT UINTN                    *NoHandles,
  OUT EFI_HANDLE                  **Buffer
  )
/*++

Routine Description:

  Function returns an array of handles that support the requested protocol 
  in a buffer allocated from pool.

Arguments:

  SearchType           - Specifies which handle(s) are to be returned.
  Protocol             - Provides the protocol to search by.   
                         This parameter is only valid for SearchType ByProtocol.
  SearchKey            - Supplies the search key depending on the SearchType.
  NoHandles            - The number of handles returned in Buffer.
  Buffer               - A pointer to the buffer to return the requested array of 
                         handles that support Protocol.

Returns:
  
  EFI_SUCCESS           - The result array of handles was returned.
  EFI_NOT_FOUND         - No handles match the search. 
  EFI_OUT_OF_RESOURCES - There is not enough pool memory to store the matching results.

--*/
{
  EFI_STATUS  Status;
  UINTN       BufferSize;

  //
  // Initialize for GrowBuffer loop
  //
  Status      = EFI_SUCCESS;
  *Buffer     = NULL;
  BufferSize  = 50 * sizeof (EFI_HANDLE);

  //
  // Call the real function
  //
  while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {
    Status = gBS->LocateHandle (
                  SearchType,
                  Protocol,
                  SearchKey,
                  &BufferSize,
                  *Buffer
                  );
  }

  *NoHandles = BufferSize / sizeof (EFI_HANDLE);
  if (EFI_ERROR (Status)) {
    *NoHandles = 0;
  }

  return Status;
}

INTN
CompareGuidx (
  IN EFI_GUID     *Guid1,
  IN EFI_GUID     *Guid2
  )
/*++

Routine Description:

  Compares to GUIDs

Arguments:

  Guid1 - guid to compare
  Guid2 - guid to compare

Returns:
  =  0  if Guid1 == Guid2
  != 0  if Guid1 != Guid2 

--*/
{
  INT32 *g1;
  INT32 *g2;
  INT32 r;

  //
  // Compare 32 bits at a time
  //
  g1  = (INT32 *) Guid1;
  g2  = (INT32 *) Guid2;

  r   = g1[0] - g2[0];
  r |= g1[1] - g2[1];
  r |= g1[2] - g2[2];
  r |= g1[3] - g2[3];

  return r;
}
// Copy from \Shell\Library\Init.c
EFI_STATUS
LibInitializeShellApplication (
  IN EFI_HANDLE                   ImageHandle,
  IN EFI_SYSTEM_TABLE             *SystemTable
  )
{
  EFI_STATUS  Status;
  EFI_HANDLE  *HandleBuffer;
  UINTN       HandleNum;
  UINTN       HandleIndex;
  EFI_GUID         SESGuid         = EFI_SE_EXT_SIGNATURE_GUID;
  
  //
  // Connect to the shell interface
  //
  Status = gBS->HandleProtocol (ImageHandle, &ShellInterfaceProtocol, (VOID *) &SI);
  if (EFI_ERROR (Status)) {
    Print (L"InitShellApp: Application not started from Shell\n");
    gBS->Exit (ImageHandle, Status, 0, NULL);
  }

  //
  // Connect to the shell environment
  //
  Status = gBS->HandleProtocol (
                ImageHandle,
                &ShellEnvProtocol,
                (VOID *) &SE2
                );
  if (EFI_ERROR (Status) || !(CompareGuid (&SE2->SESGuid, &SESGuid) == 0 &&
    (SE2->MajorVersion > EFI_SHELL_MAJOR_VER ||
      (SE2->MajorVersion == EFI_SHELL_MAJOR_VER && SE2->MinorVersion >= 
EFI_SHELL_MINOR_VER))
    )
  ) {
    Status = LocateHandle (
              ByProtocol,
              &ShellEnvProtocol,
              NULL,
              &HandleNum,
              &HandleBuffer
              );
    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 1Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, 0, NULL);
    }

    Status = EFI_NOT_FOUND;
    for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
      gBS->HandleProtocol (
           HandleBuffer[HandleIndex],
           &ShellEnvProtocol,
           (VOID *) &SE2
           );
      if (CompareGuidx (&SE2->SESGuid, &SESGuid) == 0)
	  {
        Status = EFI_SUCCESS;
        break;
      }
    }

    FreePool (HandleBuffer);

    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 2Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, Status, NULL);
    }
  }

  SE = (EFI_SHELL_ENVIRONMENT *) SE2;
  
  //
  // Done with init
  //
  return Status;
}

//
// Entry point function 
//
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
   INTN	i;
   BOOLEAN ExitMark=FALSE;
   
   gST = SystemTable;
   gBS = SystemTable -> BootServices;
   
   LibInitializeShellApplication (ImageHandle,SystemTable);
   
   while (!ExitMark) 
    {
		//Clear Screen
		gST -> ConOut -> ClearScreen(gST->ConOut);
   	    Print(L"You can break by ESC key\n");
		for (i=0;i<80*(25-1);i++)
		{
			if (SE2 -> GetExecutionBreak() ) {ExitMark=TRUE; break;}
			Print(L".");
		} 
	}
	 
  return EFI_SUCCESS;
}

 

运行结果:

BreakTest

代码:

BreakTest

Delphi发送键盘消息的例子

这是一个Delphi实现对其他窗口发送按键的例子。

首先是 keyTest,这是用来测试接收按键的程序,接收键盘发出的方向键,并将其显示在窗口上,只显示上下左右四个方向键。

keytest

其次是 SensKey ,它会首先查找窗口,然后发送按键给找到的窗口。程序是Console模式的,每隔1s发送一个方向按键。

测试时,先运行 Keytest,然后运行 SendKey,就可以看到 KeyTest 的窗口被挪到最前面,然后依次收到Sendkey 程序发出来的按键。

下载: KeyT

参考:

1.晓风缠月的博客
http://blog.sina.com.cn/s/blog_63cefe150100ogp9.html delphi虚拟键值
2.东方千树
http://hi.baidu.com/fangqianshu/item/97dc6fa46c4002e915329b21 Delphi中的OnKeyDown事件等等
OnKeyPress 只能抓到数值或字母按键及 Esc键、空白键,但不含功能键(F1-F12)
OnKeyDown 能抓到所有的键(除 Tab 键)但不能分辨「对称键」的不同
OnShortCut 能抓到所有的键(含 Tab 键)且能分辨「对称键」的不同

指定过程变量i用寄存器的问题

Pmason_rose 问我“VC中强制一个过程中变量i使用ebx咋写”?

VC2008下面先写一个简单的程序

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
int i;

for (i=0;i<10;i++)
{
printf("%d: www.lab-z.com\n",i);
}
getchar();
return 0;
}

确定上面的程序可以正常编译通过之后,可以设置 Assembler Output 中,让VC2008直接输出机器码和汇编代码:

forcereg

 

在 Debug目录下会出现 loop.cod,内容如下,可以看出使用内存变量 i 做的循环

	ff		 lea	 edi, DWORD PTR [ebp-204]
  00012	b9 33 00 00 00	 mov	 ecx, 51			; 00000033H
  00017	b8 cc cc cc cc	 mov	 eax, -858993460		; ccccccccH
  0001c	f3 ab		 rep stosd

; 9    : 	int i;
; 10   : 
; 11   : 	for (i=0;i<10;i++)

  0001e	c7 45 f8 00 00
	00 00		 mov	 DWORD PTR _i$[ebp], 0
  00025	eb 09		 jmp	 SHORT $LN3@wmain
$LN2@wmain:
  00027	8b 45 f8	 mov	 eax, DWORD PTR _i$[ebp]
  0002a	83 c0 01	 add	 eax, 1
  0002d	89 45 f8	 mov	 DWORD PTR _i$[ebp], eax
$LN3@wmain:
  00030	83 7d f8 0a	 cmp	 DWORD PTR _i$[ebp], 10	; 0000000aH
  00034	7d 1d		 jge	 SHORT $LN1@wmain

; 12   : 	{
; 13   : 		printf("%d: www.lab-z.com\n",i);

  00036	8b f4		 mov	 esi, esp
  00038	8b 45 f8	 mov	 eax, DWORD PTR _i$[ebp]
  0003b	50		 push	 eax
  0003c	68 00 00 00 00	 push	 OFFSET ??_C@_0BD@OPIHGHME@?$CFd?3?5www?4lab?9z?4com?6?$AA@
  00041	ff 15 00 00 00
	00		 call	 DWORD PTR __imp__printf
  00047	83 c4 08	 add	 esp, 8
  0004a	3b f4		 cmp	 esi, esp
  0004c	e8 00 00 00 00	 call	 __RTC_CheckEsp

; 14   : 	}

  00051	eb d4		 jmp	 SHORT $LN2@wmain
$LN1@wmain:

首先,使用寄存器变量试试,用register 关键字修饰 Int i; 修改程序

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	register int i;

	for (i=0;i<10;i++)
	{
		printf("%d: www.lab-z.com\n",i);
	}
	getchar();
	return 0;
}

查看结果

; 9    : 	register int i;
; 10   : 
; 11   : 	for (i=0;i<10;i++)

  0001e	c7 45 f8 00 00
	00 00		 mov	 DWORD PTR _i$[ebp], 0
  00025	eb 09		 jmp	 SHORT $LN3@wmain
$LN2@wmain:
  00027	8b 45 f8	 mov	 eax, DWORD PTR _i$[ebp]
  0002a	83 c0 01	 add	 eax, 1
  0002d	89 45 f8	 mov	 DWORD PTR _i$[ebp], eax
$LN3@wmain:
  00030	83 7d f8 0a	 cmp	 DWORD PTR _i$[ebp], 10	; 0000000aH
  00034	7d 1d		 jge	 SHORT $LN1@wmain

; 12   : 	{
; 13   : 		printf("%d: www.lab-z.com\n",i);

  00036	8b f4		 mov	 esi, esp
  00038	8b 45 f8	 mov	 eax, DWORD PTR _i$[ebp]
  0003b	50		 push	 eax
  0003c	68 00 00 00 00	 push	 OFFSET ??_C@_0BD@OPIHGHME@?$CFd?3?5www?4lab?9z?4com?6?$AA@
  00041	ff 15 00 00 00
	00		 call	 DWORD PTR __imp__printf
  00047	83 c4 08	 add	 esp, 8
  0004a	3b f4		 cmp	 esi, esp
  0004c	e8 00 00 00 00	 call	 __RTC_CheckEsp

; 14   : 	}

  00051	eb d4		 jmp	 SHORT $LN2@wmain

恩就是说编译器根本没有鸟我…..查看资料:

“声明为register类型的变量提示计算机这个变量应该存储于机器的硬件寄存器而不是内存中。通常,寄存器变量比存于内存中的变量访问起来效率更高。但是,编译器并不一定要理睬register关键字,如果有太多的变量被声明为register类型,那么编译器只选取前几个,将其实际存储于寄存器中,其余的就按普通变量处理。如果一个编译器自己具有一套寄存器优化方案的话,它也可能忽略register关键字,其依据是由编译器决定哪些变量春处于寄…”【参考1】

还有其他办法,就是直接插入汇编语言。修改程序如下:

// loop.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	int i;

__asm        
{   
	mov	ebx,0
next:
    push ebx
	mov i,ebx
} 
		printf("%d: www.lab-z.com\n",i);
__asm        
{	
	pop	ebx
    inc ebx
    cmp ebx,10
	jNz	next
} 

	getchar();
	return 0;
}

运行结果和之前的程序完全一样,对应的汇编和机器码

; 9    : 	int i;
; 10   : 
; 11   : __asm        
; 12   : {   
; 13   : 	mov	ebx,0

  0001e	bb 00 00 00 00	 mov	 ebx, 0
$next$5245:

; 14   : next:
; 15   :     push ebx

  00023	53		 push	 ebx

; 16   : 	mov i,ebx

  00024	89 5d f8	 mov	 DWORD PTR _i$[ebp], ebx

; 17   : } 
; 18   : 		printf("%d: www.lab-z.com\n",i);

  00027	8b f4		 mov	 esi, esp
  00029	8b 45 f8	 mov	 eax, DWORD PTR _i$[ebp]
  0002c	50		 push	 eax
  0002d	68 00 00 00 00	 push	 OFFSET ??_C@_0BD@OPIHGHME@?$CFd?3?5www?4lab?9z?4com?6?$AA@
  00032	ff 15 00 00 00
	00		 call	 DWORD PTR __imp__printf
  00038	83 c4 08	 add	 esp, 8
  0003b	3b f4		 cmp	 esi, esp
  0003d	e8 00 00 00 00	 call	 __RTC_CheckEsp

; 19   : __asm        
; 20   : {	
; 21   : 	pop	ebx

  00042	5b		 pop	 ebx

; 22   :     inc ebx

  00043	43		 inc	 ebx

; 23   :     cmp ebx,10

  00044	83 fb 0a	 cmp	 ebx, 10			; 0000000aH

; 24   : 	jNz	next

  00047	75 da		 jne	 SHORT $next$5245

; 25   : }

因此,直接嵌入汇编是一个很好的解决方法。需要注意的是堆栈的平衡以及在复杂的数据结构和操作的情况下一定要保证不能干扰参数传递。

参考1:http://bbs.csdn.net/topics/300225947

Step to UEFI (10) —- 让程序 Pause 一下的方法

很多时候我们编写的一些工具需要支持暂停的功能,比如:ls 列出的文件名时最好能够响应用户的按键,暂停一下以便用户查看结果。查看了一下Shell方面的代码,可以通过 Shell Environment 2 提供的函数来实现。

当然,我不愿意使用庞大的 Shell Library,选择性的提取一些代码就OK

//
// PauseTest.c
//
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/BaseMemoryLib.h>

#define SHELL_INTERFACE_PROTOCOL \
  { \
    0x47c7b223, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellInterfaceProtocol  = SHELL_INTERFACE_PROTOCOL;
EFI_GUID		SEGuid = EFI_SE_EXT_SIGNATURE_GUID;
//
// The shell environment is provided by a driver.  The shell links to the
// shell environment for services.  In addition, other drivers may connect
// to the shell environment and add new internal command handlers, or
// internal protocol handlers.
//
#define SHELL_ENVIRONMENT_INTERFACE_PROTOCOL \
  { \
    0x47c7b221, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellEnvProtocol = SHELL_ENVIRONMENT_INTERFACE_PROTOCOL;

#define EFI_OUTPUT_PAUSE    0x00000002

typedef struct {
  SHELLENV_EXECUTE          Execute;  // Execute a command line
  SHELLENV_GET_ENV          GetEnv;   // Get an environment variable
  SHELLENV_GET_MAP          GetMap;   // Get mapping tables
  SHELLENV_ADD_CMD          AddCmd;   // Add an internal command handler
  SHELLENV_ADD_PROT         AddProt;  // Add protocol info handler
  SHELLENV_GET_PROT         GetProt;  // Get the protocol ID
  SHELLENV_CUR_DIR          CurDir;
  SHELLENV_FILE_META_ARG    FileMetaArg;
  SHELLENV_FREE_FILE_LIST   FreeFileList;

  //
  // The following services are only used by the shell itself
  //
  SHELLENV_NEW_SHELL        NewShell;
  SHELLENV_BATCH_IS_ACTIVE  BatchIsActive;

  SHELLENV_FREE_RESOURCES   FreeResources;
} EFI_SHELL_ENVIRONMENT;

EFI_SHELL_INTERFACE             *SI;
EFI_SHELL_ENVIRONMENT           *SE;
EFI_SHELL_ENVIRONMENT2          *SE2;

EFI_BOOT_SERVICES     *gBS;
EFI_RUNTIME_SERVICES  *gRT;
EFI_SYSTEM_TABLE      *gST;

//Copy from \Shell\Library\Misc.c
BOOLEAN
GrowBuffer (
  IN OUT EFI_STATUS   *Status,
  IN OUT VOID         **Buffer,
  IN UINTN            BufferSize
  )
/*++

Routine Description:

  Helper function called as part of the code needed
  to allocate the proper sized buffer for various 
  EFI interfaces.

Arguments:

  Status      - Current status

  Buffer      - Current allocated buffer, or NULL

  BufferSize  - Current buffer size needed
    
Returns:
    
  TRUE - if the buffer was reallocated and the caller 
  should try the API again.

--*/
{
  BOOLEAN TryAgain;

  //
  // If this is an initial request, buffer will be null with a new buffer size
  //
  if (NULL == *Buffer && BufferSize) {
    *Status = EFI_BUFFER_TOO_SMALL;
  }
  //
  // If the status code is "buffer too small", resize the buffer
  //
  TryAgain = FALSE;
  if (*Status == EFI_BUFFER_TOO_SMALL) {

    if (*Buffer) {
      FreePool (*Buffer);
    }

    *Buffer = AllocateZeroPool (BufferSize);

    if (*Buffer) {
      TryAgain = TRUE;
    } else {
      *Status = EFI_OUT_OF_RESOURCES;
    }
  }
  //
  // If there's an error, free the buffer
  //
  if (!TryAgain && EFI_ERROR (*Status) && *Buffer) {
    FreePool (*Buffer);
    *Buffer = NULL;
  }

  return TryAgain;
}

//Copy from \Shell\Library\Handle.c
EFI_STATUS
LocateHandle (
  IN EFI_LOCATE_SEARCH_TYPE       SearchType,
  IN EFI_GUID                     * Protocol OPTIONAL,
  IN VOID                         *SearchKey OPTIONAL,
  IN OUT UINTN                    *NoHandles,
  OUT EFI_HANDLE                  **Buffer
  )
/*++

Routine Description:

  Function returns an array of handles that support the requested protocol 
  in a buffer allocated from pool.

Arguments:

  SearchType           - Specifies which handle(s) are to be returned.
  Protocol             - Provides the protocol to search by.   
                         This parameter is only valid for SearchType ByProtocol.
  SearchKey            - Supplies the search key depending on the SearchType.
  NoHandles            - The number of handles returned in Buffer.
  Buffer               - A pointer to the buffer to return the requested array of 
                         handles that support Protocol.

Returns:
  
  EFI_SUCCESS           - The result array of handles was returned.
  EFI_NOT_FOUND         - No handles match the search. 
  EFI_OUT_OF_RESOURCES - There is not enough pool memory to store the matching results.

--*/
{
  EFI_STATUS  Status;
  UINTN       BufferSize;

  //
  // Initialize for GrowBuffer loop
  //
  Status      = EFI_SUCCESS;
  *Buffer     = NULL;
  BufferSize  = 50 * sizeof (EFI_HANDLE);

  //
  // Call the real function
  //
  while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {
    Status = gBS->LocateHandle (
                  SearchType,
                  Protocol,
                  SearchKey,
                  &BufferSize,
                  *Buffer
                  );
  }

  *NoHandles = BufferSize / sizeof (EFI_HANDLE);
  if (EFI_ERROR (Status)) {
    *NoHandles = 0;
  }

  return Status;
}

INTN
CompareGuidx (
  IN EFI_GUID     *Guid1,
  IN EFI_GUID     *Guid2
  )
/*++

Routine Description:

  Compares to GUIDs

Arguments:

  Guid1 - guid to compare
  Guid2 - guid to compare

Returns:
  =  0  if Guid1 == Guid2
  != 0  if Guid1 != Guid2 

--*/
{
  INT32 *g1;
  INT32 *g2;
  INT32 r;

  //
  // Compare 32 bits at a time
  //
  g1  = (INT32 *) Guid1;
  g2  = (INT32 *) Guid2;

  r   = g1[0] - g2[0];
  r |= g1[1] - g2[1];
  r |= g1[2] - g2[2];
  r |= g1[3] - g2[3];

  return r;
}
// Copy from \Shell\Library\Init.c
EFI_STATUS
LibInitializeShellApplication (
  IN EFI_HANDLE                   ImageHandle,
  IN EFI_SYSTEM_TABLE             *SystemTable
  )
{
  EFI_STATUS  Status;
  EFI_HANDLE  *HandleBuffer;
  UINTN       HandleNum;
  UINTN       HandleIndex;
  EFI_GUID         SESGuid         = EFI_SE_EXT_SIGNATURE_GUID;
  
  //
  // Connect to the shell interface
  //
  Status = gBS->HandleProtocol (ImageHandle, &ShellInterfaceProtocol, (VOID *) &SI);
  if (EFI_ERROR (Status)) {
    Print (L"InitShellApp: Application not started from Shell\n");
    gBS->Exit (ImageHandle, Status, 0, NULL);
  }

  //
  // Connect to the shell environment
  //
  Status = gBS->HandleProtocol (
                ImageHandle,
                &ShellEnvProtocol,
                (VOID *) &SE2
                );
  if (EFI_ERROR (Status) || !(CompareGuid (&SE2->SESGuid, &SESGuid) == 0 &&
    (SE2->MajorVersion > EFI_SHELL_MAJOR_VER ||
      (SE2->MajorVersion == EFI_SHELL_MAJOR_VER && SE2->MinorVersion >= 
EFI_SHELL_MINOR_VER))
    )
  ) {
    Status = LocateHandle (
              ByProtocol,
              &ShellEnvProtocol,
              NULL,
              &HandleNum,
              &HandleBuffer
              );
    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 1Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, 0, NULL);
    }

    Status = EFI_NOT_FOUND;
    for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
      gBS->HandleProtocol (
           HandleBuffer[HandleIndex],
           &ShellEnvProtocol,
           (VOID *) &SE2
           );
      if (CompareGuidx (&SE2->SESGuid, &SESGuid) == 0)
	  {
        Status = EFI_SUCCESS;
        break;
      }
    }

    FreePool (HandleBuffer);

    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 2Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, Status, NULL);
    }
  }

  SE = (EFI_SHELL_ENVIRONMENT *) SE2;
  
  //
  // Done with init
  //
  return Status;
}

//
// Entry point function 
//
EFI_STATUS
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
   INTN	i;

  Print(L"You can't Pause by Tab key\n");
  for (i=0;i<1000;i++)
    {
	Print(L".");
    }
  Print(L".\n");
  gBS = SystemTable -> BootServices;

  LibInitializeShellApplication (ImageHandle,SystemTable);
  SE2->SetKeyFilter(SE2->GetKeyFilter() | EFI_OUTPUT_PAUSE);
  
  Print(L"You can Pause by Tab key");
  for (i=0;i<1000;i++)
    {
  	Print(L".");
    }

  return EFI_SUCCESS;
}

 

上面的代码演示了使用 Pause Break键来暂停输出的功能。

PauseTest

代码在这里下载:

PauseTest

关于前面提到的 VOL 命令的问题

前面介绍过一些Shell下常用的命令,提到了查看当前 volume 大小的命令 VOL. 在使用中,会遇到一个奇怪的问题,感觉上有时候它能输出当前盘的大小,而有时候输出的是Partition的大小,具体的原因是什么呢?下载 VOL 的代码,进行查看注意到下面的位置:

Status = RootFs->GetInfo (RootFs, &gEfiFileSystemInfoGuid, &Size, VolumeInfo);

就是说VOL命令是基于File System的,显示出来的是文件系统能够识别出来的大小。

做一个简单的实验,将U盘进行分区,下面是只有一个分区的情况

Capture

实验的结果如下:

Volume NEW VOLUME (rw)
1,003,487,232 bytes total disk space
959,131,648 bytes available on disk
4,096 bytes in each allocation unit

再使用工具将这个U盘分区

Capture2

实验的结果如下:

Volume has no label (rw)
411,009,024 bytes total disk space
411,000,832 bytes available on disk
8,192 bytes in each allocation unit

显示出来的只是有文件系统的那个分区的大小。

因此,之前实验遇到的问题是:当查看的盘上只有一个分区,并且这个分区占满了全部空间的时候,显示的也是盘的大小;如果上面的分区没有占满盘的话,显示出来的只有分区大小了。

vol.c 可以在这里下载 vol

小游戏 2048 Delphi 命令行复刻版

最近的一款名为 2048 的小游戏很火

可以在 http://gabrielecirulli.github.io/2048/ 这里看到一个网页版,基于 HTML5,只需要用方向键让两两相同的数字碰撞就会诞生一个翻倍的数字,初始数字由 2 或者 4 构成,直到游戏界面全部被填满,游戏结束。

0

我使用 delphi 编写了一个 console 版的,速度更快,不过界面没有原来的版本好看 :( 和人类打交道不是我的长项……

2048

2048

源程序2048re

分离GOP和VBT文件的工具

一个用于分离GOP和VBT文件的工具

这个工具是用来分离Intel Baytrail公版BIOS中的VBT和GOP文件的。其中的GOP是传统意义上的VBIOS,VBT文件是他的配置文件。在之前,配置

信息通常会写入到VBIOS中,但是在BayTrail上他们是分开独立的。

用法:

BTYGVS [文件名]

比如:BTYGVS IFWI_2013_10_01 即可在当前目录下生成这个BIOS中

包括的GOP和VBT。

gvs

下载

bytgvs

介绍几个 UEFI Shell下的命令 (下)

继续介绍 Shell 下面的命令。

ECHO 和DOS下的这个命令相同,这是用来回显“字符串的”,比如下面的例子

echo

MAP 这是一个“定义用户名和设备handle映射关系”的命令。最常见的用途就是给支持文件protocol的设备分配一个盘符,比如: fs0:

另外,最常见的用法是当你进入shell之后发现忘记插入U盘,插入之后U盘的盘符不会马上可以使用,这时候可以使用 map -r 一下,让他识别。

map1

SET 和DOS下的这个命令相同,设置环境变量。SPEC中提到,不加额外参数设定的变量是“易非失”的,这次启动在,断电再上电还是在(我试验过)。

如果使用 -v 可以设置一个“易失”的变量,断电一次就没了。前面有 “×” 表示这个变量是易失的。

set1

VOL 显示当前的硬盘/分区容量,如果你发现Windows无法正常安装不妨试试这个命令检查一下是否使用了错误的eMMC. (后面会查看一下这个命令的代码确定究竟查看的是硬盘容量还是分区容量)

vol

VER 显示当前使用的UEFI版本信息

ver

TIME 显示和设置当前的时间信息(不知道如何设置时区?)

time

TYPE 和DOS下的这个命令相同,显示文件的内容,支持普通的ASCII和UNICODE编码。

type

LZMA 压缩的例子

前一段时间研究了一下 LZ77 算法,后来又看了一下它的改进版本 LZMA。虽然基本思想已经完全领悟,但是要想具体写出代码还是很有难度,直接研究实现算法又被很多细节阻挡。好在时代不同了,虽然你无法写出具体代码,但是也有现成的库供你调用。

目前开源的比较好用的就是 7-zip 了,这里可以看到中文的介绍:http://sparanoid.com/lab/7z/

同时他还提供了 SDK 供以调用 http://sparanoid.com/lab/7z/sdk.html 。

对于 Delphi 的用户,能够使用的 SDK 在 http://www.birtles.org.uk/programming/ 这里可以下载到。是原生的 Delphi 编写无需第三方库。为了测试我编写了一个Console模式下压缩的小程序,并且在程序中我避免使用这个库内部定义的 Stream 而是直接使用 TMemoryStream。

program LZMAencoder;

{$APPTYPE CONSOLE}

{$R *.res}

uses
System.SysUtils, Windows, Classes,ULZMAEncoder,ULZMACommon;

var
FInput,Foutput:TMemoryStream; //直接使用基础的 MemoryStream
encoder:TLZMAEncoder; //编码器
i:integer; //算法方面的要求,最后要补零。具体请查看LZ77描述
begin
FInput:=TMemoryStream.Create; //创建和读取一个文件作为压缩的源
FInput.LoadFromFile('C:\Users\Administrator\Documents\RAD Studio\Projects\LZMAEncoder\original.txt');

encoder:=TLZMAEncoder.Create;

encoder.SetAlgorithm(2); //设置压缩比,看代码似乎没有真正实现
encoder.SetDictionarySize(1 shl 23); //设置字典大小
encoder.SeNumFastBytes(128); //不知道这个是什么
encoder.SetMatchFinder(1); //找到1个匹配
encoder.SetLcLpPb(3, 0, 2); //应该是LZ77算法用到的几个参数
encoder.SetEndMarkerMode(false); //是否写入流结束标志。因为压缩之后的头上有大小
//所以这里完全不用写入
FOutput:=TMemoryStream.Create;
encoder.WriteCoderProperties(FOutput); //写入头信息
for i := 0 to 7 do
WriteByte(FOutput,(FInput.Size shr (8 * i)) and $FF); //补全完整流

encoder.Code(Finput, FOutput, -1, -1); //解压过程
encoder.free;
FOutput.SaveToFile('C:\Users\Administrator\Documents\RAD Studio\Projects\LZMAEncoder\result.7z');
FOutput.Destroy;
Finput.Destroy;
readln;
end.

压缩结果可以直接用 7-zip 打开(WinZip, WinRar不行)。

Capture

原始的  LZMA 代码 LZMA.442b

本文提到的例子:LZMAEncoder