Step to UEFI (83) BlockIo Protocol

“因为硬盘是一种块设备,所以每个硬盘设备(硬盘设备包括分区设备)控制器都安装有一个 BlockIo 实例,一个 BlockIo2实例。BlockIo 提供了访问设备的阻塞函数,BlockIo2提供了访问设备的异步函数”【参考1】

blk2

blk1

这里提供一个枚举BlockIo,然后显示每一个 Media 属性的例子:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include <Protocol/BlockIo.h>

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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
    EFI_STATUS				Status;
    UINTN					HandleCount,HandleIndex;
    EFI_HANDLE              *BlockControllerHandles = NULL;	
	EFI_BLOCK_IO_PROTOCOL   *BlockIo;
	
	//找到全部有 BlockIo Protocol 的Device
    Status = gBS->LocateHandleBuffer(
            ByProtocol,
            &gEfiBlockIoProtocolGuid,
            NULL,
            &HandleCount,
            &BlockControllerHandles);  

   if (!EFI_ERROR(Status)) {
        //逐个打开 
        for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
            /*打开EFI_BLOCK_IO_PROTOCOL  */ 
            Status = gBS->HandleProtocol(
                    BlockControllerHandles[HandleIndex],
                    &gEfiBlockIoProtocolGuid,
                    (VOID**)&BlockIo);
			//显示信息		
			Print(L"-->[Device]<--:%d\n",HandleIndex);
			Print(L"MediaId       :%0x\n",BlockIo->Media->MediaId);
    		Print(L"RemovableMedia:%0x\n",BlockIo->Media->RemovableMedia);
    		Print(L"MediaPresent  :%0x\n",BlockIo->Media->MediaPresent);
    		Print(L"ReadOnly      :%0x\n",BlockIo->Media->ReadOnly); 
    		Print(L"WriteCaching  :%0x\n",BlockIo->Media->WriteCaching);
    		Print(L"BlockSize     :%0x\n",BlockIo->Media->BlockSize);
    		Print(L"IoAlign       :%0x\n",BlockIo->Media->IoAlign);
    		Print(L"LastBlock     :%0x\n",BlockIo->Media->LastBlock);
    		Print(L"LogicalPartition :%0x\n",BlockIo->Media->LogicalPartition);
    		Print(L"LowestAlignedLba :%0x\n",BlockIo->Media->LowestAlignedLba);
    		Print(L"LogicalBlocksPerPhysicalBlock   : %0x\n",
			BlockIo->Media->LogicalBlocksPerPhysicalBlock);
    		Print(L"OptimalTransferLengthGranularity: %0x\n",
			BlockIo->Media->OptimalTransferLengthGranularity);
        }	//for (HandleIndex = 0;
		
        gBS->FreePool(BlockControllerHandles);
    }			
  return EFI_SUCCESS;
}

 

在 Nt32 虚拟机中运行的结果

blockioa

完整的代码和程序下载:

BIOTest

参考
1. UEFI 原理与编程 P139

一个计算时间的批处理

有时候,我们需要简单的测量一下某个程序经过的时间,经过搜索在网上【参考1】,找到下面的批处理可以完成这个要求。

@echo off
set ns=0
rem Show start time
set time1=%time%
echo Start time is %time1%
call :time2sec %time1%
set t1=%ns%


rem Do what you want


rem Show end time
set time2=%time%
echo End time is ?%time2%
call :time2sec %time2%
set t2=%ns%
rem Calculate
set /a tdiff=%t2% - %t1%
echo diff %time1% from %time2% = %tdiff% seconds.
goto :eof

:time2sec
rem CDonvert time to seconds. Save to ns
set tt=%1
set hh=%tt:~0,2%
set mm=%tt:~3,2%
set ss=%tt:~6,2%
set /a ns=(%hh%*60+%mm%)*60+%ss%


goto :eof

 

比如,我用这个工具来测试Build BIOS 的时间:

CLean 之后,build 要花费5分钟
clean

再次编译,build只需要不到一分钟
noclean

参考:
1.http://zhidao.baidu.com/link?url=dL8oq_ik4tX5St4YkwQk4vz8HjYGIeFa6ybUs21BI9h6VdSdx7B7BpqdaiVIsNr8rKNlYoRJZ4rIGpxwRfCB3a

Step to UEFI (82) NT32Pkg的Debug Message

最近在查看EDKII代码的时候忽然有一个奇怪的想法:在运行模拟器的时候(Build Run),我们可以在编译窗口看到很多输出的Debug 信息,那么我们是否可以在自己编写的Application中输出这样的信息?

最直接的想法是在 Application中调用 gWinNt ,但是如果要用这个东西,需要定义很多文件头,最麻烦的是这些头文件最后都要使用 Windows.h ,编译的时候总是无法通过。另外的方法是,在 Application 中调用诸如 WinNtThunkDxe 或者 WinNtSerialIoDxe 这样的Protocol,但是在编译使同样会遇到有上面的问题。我尝试了很多次都没有成功,最后只好放弃。

换一种思路,我们可以在NT32Pkg中留下可供调用的函数,然后在 Application 中Call这个函数。

最简单的功能就是用于系统重启的 gST->Reset 。经过查找,模拟器中实现这个功能的代码在 \Nt32Pkg\ResetRuntimeDxe\reset.c 。头定义如下

VOID
EFIAPI
WinNtResetSystem (
  IN EFI_RESET_TYPE   ResetType,
  IN EFI_STATUS       ResetStatus,
  IN UINTN            DataSize,
  IN VOID             *ResetData OPTIONAL
  )
/*++

Routine Description:

  TODO: Add function description

Arguments:

  ResetType   - TODO: add argument description
  ResetStatus - TODO: add argument description
  DataSize    - TODO: add argument description
  ResetData   - TODO: add argument description

Returns:

  EFI_SUCCESS - TODO: Add description for return value

--*/

 

实现功能的代码只有一行 gWinNt->ExitProcess (0)。只要把这些语句注释掉,替换为我们的输出代码即可。参考 \Nt32Pkg\Library\PeiNt32OemHookStatusCodeLib\Nt32OemHookStatusCodeLib.c
,对编译窗口输出的语句如下:

 //
  // Callout to standard output.
  //
  mWinNt->WriteFile (
            mStdOut,
            Buffer,
            (DWORD)CharCount,
            (LPDWORD)&CharCount,
            NULL
            );

 

搬过来,写成下面的形式:

VOID
EFIAPI
WinNtResetSystem (
  IN EFI_RESET_TYPE   ResetType,
  IN EFI_STATUS       ResetStatus,
  IN UINTN            DataSize,
  IN VOID             *ResetData OPTIONAL
  )
/*++

Routine Description:

  TODO: Add function description

Arguments:

  ResetType   - TODO: add argument description
  ResetStatus - TODO: add argument description
  DataSize    - TODO: add argument description
  ResetData   - TODO: add argument description

Returns:

  EFI_SUCCESS - TODO: Add description for return value

--*/
{
  CHAR8			 *R="www.lab-z.com \n\r";	
  UINTN           CharCount=AsciiStrLen(R);
  
  //
  // Cache of standard output handle .
  //
	HANDLE                      mStdOut;
  
  //
  // Cache standard output handle.
  //
  mStdOut = gWinNt->GetStdHandle (STD_OUTPUT_HANDLE);  
  
  //
  // Callout to standard output.
  //
  gWinNt->WriteFile (
            mStdOut,
            R,
            (DWORD)CharCount,
            (LPDWORD)&CharCount,
            NULL
            );
			
  //
  // BUGBUG Need to kill all console windows later
  //
  //
  // Discard ResetType, always return 0 as exit code
  //
  //gWinNt->ExitProcess (0);

  //
  // Should never go here
  //
  //ASSERT (FALSE);
}

 

特别注意:字符串是 Ascii 定义的,末尾必须是 \r\n ,否则不会立即显示。

最后运行结果,只要在模拟器中输入 reset 即可看到字符串,正常的动作应该是退出模拟器。

nt32a

nt32b

因为我修改掉了退出的方法,每次需要在编译窗口用 ctrl+c来结束模拟器了。

这里只是一个Demo,最好还是重新在EDKII代码中定义一个用来直接输出的接口。

Compiler Warning C4131

在尝试编译老旧的代码时,你可能遇到 C4131 Warning, 给出来的提示是 “uses old-style declarator”。 这是因为你的代码使用了旧式的生命方式

比如:

// C4131.c
// compile with: /W4 /c
void addrec( name, id ) // C4131 expected
char *name;
int id;
{ }

 

这个定义会导致上面的 warning。修改为下面的即可

 void addrec( char *name, int id )
 { }

 

更多信息可以在
https://msdn.microsoft.com/en-us/library/b92s55e9(v=vs.90).aspx 看到

Debug Message 标记工具

对于庞大的代码,串口 Message 提供了最好的追踪方法。但是很多时候,我们的代码会有重复的文件,在对照阅读的时候就会非常麻烦。一种方法是之前文章介绍过的使用 __FILE__ 的方法,唯一的问题是这个宏经常导致代码在Debug 模式下爆掉(很大原因是因为Debug模式下这个宏会加入路径信息)。因此,编写这个工具,具体的原理是:打开所有的 C 文件,查找 DEBUG宏,然后在它的字符串起始处加入 “(序号)”这样的标记。序号是由 0-9和A-Z组成的。更改代码之后,再次编译,串口输出的内容就有序号+原先的字符串,能够很方便的分析出来源。

特别注意:某些情况下可能会导致源文件的错误,这是目前分析的手法决定的。不过从我的实践来看,EDK代码不会有问题,某些工程文件可能会有1到2处标记错误,手动修改去掉即可。

这是我在某个BIOS工程上直接实验的结果:

dm

可执行程序和完整代码下载

DebugMark

MPR121 触摸传感器模块

MPR121 是一款触摸传感器芯片,原理是通过检测电容变化来判断当前是否有触摸(接近)。

主要电器特性如下【参考1】:
1. 工作电压1.71-3.6v(芯片工作电压)
2. 通讯接口为 I2C
3. 12个检测端口
4. 带有1个 IRQ端口

我是在Taobao购买的模块,价格是16元【参考3】。经过搜索,这是仿sparkfun的,更多资料可以在【参考2】看到。

模块长得下面这样,有一个降压芯片,看起来可以直接使用 5V 供电。上面有12个口,可以接12个触摸按钮。IRQ 的作用是发出中断通知上面有触摸。ADD是芯片的I2C地址选择,接GND VDD SDA或者 SCL 地址分别是 0x5A 0x5B 0x5C 和 0x5D【来自参考4】。

image001

图片来自【参考2】

下面的代码可以正常工作(卖家的例程,有一些修改可以在 1.6.0 上编译通过)

#include "mpr121.h"
#include <Wire.h>

#define SENSORS       13
#define TOU_THRESH    0x1F
#define REL_THRESH    0x1A
#define PROX_THRESH   0x3f
#define PREL_THRESH   0x3c

// variables: capacitive sensing
bool touchStates[SENSORS];    // holds the current touch/prox state of all sensors
bool activeSensors[SENSORS] = {1,1,1,1,1,1,1,1,1,1,1,1,1}; // holds which sensors are active (0=inactive, 1=active)
bool newData = false;         // flag that is set to true when new data is available from capacitive sensor
int irqpin = 2;               // pin that connects to notifies when data is available from capacitive sensor

void setup(){

  // attach interrupt to pin - interrupt 1 is on pin 2 of the arduino (confusing I know)
  attachInterrupt(0, dataAvailable, FALLING);

  // set-up the Serial and I2C/Wire connections
  Serial.begin(9600);
  Wire.begin();

  // set the registers on the capacitive sensing IC
  setupCapacitiveRegisters();

}

void loop(){
  readCapacitiveSensor();
}

/**
 * dataAvailable Callback method that runs whenever new data becomes available on from the capacitive sensor. 
 *   This method was attached to the interrupt on pin 2, and is called whenever that pins goes low.
 */
void dataAvailable() {
  newData = true;
}

/**
 * readCapacitiveSensor Reads the capacitive sensor values from the MP121 IC. It makes a request to
 *   the sensor chip via the I2C/Wire connection, and then parses the sensor values which are stored on
 *   the first 13 bits of the 16-bit response msg.
 */
void readCapacitiveSensor(){
  if(newData){    
            Serial.println("yes");      
    //read the touch state from the MPR121
    Wire.requestFrom(0x5A,2); 
    byte tLSB = Wire.read();
    byte tMSB = Wire.read();
    uint16_t touched = ((tMSB << 8) | tLSB); //16bits that make up the touch states

    for (int i = 0; i < SENSORS; i++){  // Check what electrodes were pressed
      if (activeSensors[i] == 0) continue;
      char sensor_id [] = {'\0','\0','\0'};
      switch (i) {
        case 12:
          sensor_id[0] = 'P';
          break;
        default:
          if (i < 10) {
            sensor_id[0] = char( i+48 );
          } 
          else if (i < 12) {
            sensor_id[0] = char('1');
            sensor_id[1] = char( ( i % 10 ) + 48 );
          } 
      }
      if (sensor_id != '\0') {
        // read the humidity level

        // if current sensor was touched (check appropriate bit on touched var)
        if(touched & (1<<i)){      
          // if current pin was not previously touched send a serial message
          if(touchStates[i] == 0){          
            Serial.print(sensor_id);        
            Serial.print(":");
            Serial.println("1");
          } 
          touchStates[i] = 1;      
        } else {
          // if current pin was just touched send serial message
          if(touchStates[i] == 1){
            Serial.print(sensor_id);
            Serial.print(":");
            Serial.println("0");
          }
          touchStates[i] = 0;
        }        
      }
    }
    newData = false;
  }
}

/**
 * setupCapacitiveRegisters Updates all of configurations on the MP121 capacitive sensing IC. This includes
 *   setting levels for all filters, touch and proximity sensing activation and release thresholds, debounce,
 *   and auto-configurations options. At the end it activates all of the electrodes.
 */
void setupCapacitiveRegisters(){

  set_register(0x5A, ELE_CFG, 0x00); 
  
  // Section A - filtering when data is > baseline.
    // touch sensing
    set_register(0x5A, MHD_R, 0x01);
    set_register(0x5A, NHD_R, 0x01);
    set_register(0x5A, NCL_R, 0x00);
    set_register(0x5A, FDL_R, 0x00);

    // prox sensing 
    set_register(0x5A, PROX_MHDR, 0xFF);
    set_register(0x5A, PROX_NHDAR, 0xFF);
    set_register(0x5A, PROX_NCLR, 0x00);
    set_register(0x5A, PROX_FDLR, 0x00);

  // Section B - filtering when data is < baseline.
    // touch sensing
    set_register(0x5A, MHD_F, 0x01);
    set_register(0x5A, NHD_F, 0x01);
    set_register(0x5A, NCL_F, 0xFF);
    set_register(0x5A, FDL_F, 0x02);
  
    // prox sensing
    set_register(0x5A, PROX_MHDF, 0x01);
    set_register(0x5A, PROX_NHDAF, 0x01);
    set_register(0x5A, PROX_NCLF, 0xFF);
    set_register(0x5A, PROX_NDLF, 0xFF);

  // Section C - Sets touch and release thresholds for each electrode
    set_register(0x5A, ELE0_T, TOU_THRESH);
    set_register(0x5A, ELE0_R, REL_THRESH);
   
    set_register(0x5A, ELE1_T, TOU_THRESH);
    set_register(0x5A, ELE1_R, REL_THRESH);
    
    set_register(0x5A, ELE2_T, TOU_THRESH);
    set_register(0x5A, ELE2_R, REL_THRESH);
    
    set_register(0x5A, ELE3_T, TOU_THRESH);
    set_register(0x5A, ELE3_R, REL_THRESH);
    
    set_register(0x5A, ELE4_T, TOU_THRESH);
    set_register(0x5A, ELE4_R, REL_THRESH);
    
    set_register(0x5A, ELE5_T, TOU_THRESH);
    set_register(0x5A, ELE5_R, REL_THRESH);
    
    set_register(0x5A, ELE6_T, TOU_THRESH);
    set_register(0x5A, ELE6_R, REL_THRESH);
    
    set_register(0x5A, ELE7_T, TOU_THRESH);
    set_register(0x5A, ELE7_R, REL_THRESH);
    
    set_register(0x5A, ELE8_T, TOU_THRESH);
    set_register(0x5A, ELE8_R, REL_THRESH);
    
    set_register(0x5A, ELE9_T, TOU_THRESH);
    set_register(0x5A, ELE9_R, REL_THRESH);
    
    set_register(0x5A, ELE10_T, TOU_THRESH);
    set_register(0x5A, ELE10_R, REL_THRESH);
    
    set_register(0x5A, ELE11_T, TOU_THRESH);
    set_register(0x5A, ELE11_R, REL_THRESH);

  // Section D - Set the touch filter Configuration
    set_register(0x5A, FIL_CFG, 0x04);  

  // Section E - Set proximity sensing threshold and release
    set_register(0x5A, PRO_T, PROX_THRESH);   // sets the proximity sensor threshold
    set_register(0x5A, PRO_R, PREL_THRESH);   // sets the proximity sensor release

  // Section F - Set proximity sensor debounce
    set_register(0x59, PROX_DEB, 0x50);  // PROX debounce

  // Section G - Set Auto Config and Auto Reconfig for prox sensing
    set_register(0x5A, ATO_CFGU, 0xC9);  // USL = (Vdd-0.7)/vdd*256 = 0xC9 @3.3V   
    set_register(0x5A, ATO_CFGL, 0x82);  // LSL = 0.65*USL = 0x82 @3.3V
    set_register(0x5A, ATO_CFGT, 0xB5);  // Target = 0.9*USL = 0xB5 @3.3V
    set_register(0x5A, ATO_CFG0, 0x0B);

  // Section H - Start listening to all electrodes and the proximity sensor
    set_register(0x5A, ELE_CFG, 0x3C);
}

/**
 * set_register Sets a register on a device connected via I2C. It accepts the device's address, 
 *   register location, and the register value.
 * @param address The address of the I2C device
 * @param r       The register's address on the I2C device
 * @param v       The new value for the register
 */
void set_register(int address, unsigned char r, unsigned char v){
  Wire.beginTransmission(address);
  Wire.write(r);
  Wire.write(v);
  Wire.endTransmission();
}

 

运行结果:

image004

完整的代码下载

MPR121

参考:
1. http://wenku.baidu.com/link?url=77EtEpflHOBF9LmqwOvIwe5ONZ6I548h4BcBk4Ep1XWVO_RVDj9fycwoku44RENseV48lzvnrnDasY3UAMHsBuuU7yVdxFsAfxq-zbDiEhy MPR121中文数据手册
2. https://learn.sparkfun.com/tutorials/mpr121-hookup-guide
3. https://item.taobao.com/item.htm?spm=a1z09.2.0.0.kpoNRc&id=19968128319&_u=pkf8s90f4c
4. MPR121 DataSheet

===============================================================================
额外说一下这个传感器在 Pro Micro 上的连接方法:

1. Pro Micro 的中断:“The Pro Micro has five external interrupts, which allow you to instantly trigger a function when a pin goes either high or low (or both). If you attach an interrupt to an interrupt-enabled pin, you’ll need to know the specific interrupt that pin triggers: pin 3 maps to interrupt 0, pin 2 is interrupt 1, pin 0 is interrupt 2, pin 1 is interrupt 3, and pin 7 is interrupt 4.”
推荐 使用 Pin7-Interrupt 4
2. Pro Micro Pin2- SDA Pin3-SCL

VC 中的 __FILE__ 宏

VC 中有一个 __FILE__ 宏定义,表示当前文件,比如,下面的程序就可以输出编译期的源文件名。

#include "stdafx.h"
#include "stdio.h"

int _tmain(int argc, _TCHAR* argv[])
{
	printf(__FILE__);
	getchar();
	return 0;
}

 

我们在BIOS中经常看到的 ASSERT 就使用这个宏来准确的输出出现错误的位置。

此外,在 Release 模式下这个宏只包括文件名,Debug 模式下这个宏会输出完整的路径。

debug

release

从屏幕输出的方法

之前,我写过一篇“Arduino 打造自动输入器”【参考1】。文章的应用场景是“某些时候因为一些特殊的原因,使得我们不能直接使用U盘之类的存储设备”。在这样的条件下,我们可以用 Arduino打造一个虚拟键盘,一个字节一个字节的敲入我们期望的程序。有了输入内容的方法,接下来的问题是:如何把需要的内容输出出来。本文给出一个方法:将要输出的文件,转化为二维码通过屏幕输出,用户再用手机对着屏幕进行识别。
大多数人都不希望看着长篇的代码,这里我只是列出来大概的思路,有需要的朋友可以在后面下载到Delphi编写的代码。
具体方法是:将欲传文件切为一定的大小(决定大小的是QR Code的容量【参考2】,屏幕分辨率和你手机的摄像头的分辨率),然后通过程序将十六进制表示的字节转换为字符串,再通过程序生成一张张二维码,之后显示在屏幕上就可以通过手机来识别,识别之后可以发送到邮箱,再用另外的程序把所有的文件“粘”起来就恢复原样了。
下面是一个具体的操作例子。
首先选择文件,这里我们选一个比较小的文件。

image001

程序会将文件转换为 QR_Code,这里我直接生成的是BMP,体积较大。切成了 6 个图片。

image002

image003

然后一边显示,一边用手机拍照(半自动)。

image004

这个照片中展示的是 ZXing 软件进行识别,不过最后我发现小米手机的二维码扫描工具,感觉识别率和容错能力更好。

image005

这里手工粘贴下来(我使用的二维码生成控件有问题,无法生成数字0 ,所以我代码上用 “-” 来替代了所有的 “0”)。
image006

把每个文件的扫描结果单独存放在一个个*.labz文件中,使用替换方法将“-”替换为“0”,再使用另外的Delphi编写的工具把他们转换为十六进制。
image007

替换之后会生成 0001.bin 这样的文件,最终用批处理将他们粘在一起就好了。

image008

最终结果和原始文件没有差别。

image009

完整的工具下载
Demo

File2Image

TxtToBin

参考:
1. http://www.lab-z.com/ardkey/ Arduino 打造自动输入器
2. GBT 18284-2000 快速响应矩阵码

Step to UEFI Tips

最近写程序遇到了一个非常奇怪的问题,化简问题如下:
1. 定义 CHAR8 大小的 Buffer[4]
2. 其中赋值为 128 82 0 0
3. 将他们通过下面的公式合成为一个 INT32
Buffer[0]+(Buffer[1]<<8) +(Buffer[2]<<16)+ (Buffer[3]<<24); 4. 我们期望得到 0x5280=21120, 但是给出来的结果却是 20864 编写程序如下,我们从命令行接收参数,然后转换为数值。

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>


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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	CHAR8	Buffer[4];
	UINTN	FileSize=0;

	Print(L"Argv [%s %s %s %s]\n",
			Argv[1],Argv[2],Argv[3],Argv[4]);	
			
	#pragma warning(disable:4244)
	Buffer[0]=StrDecimalToUintn(Argv[1]);
	Buffer[1]=StrDecimalToUintn(Argv[2]);
	Buffer[2]=StrDecimalToUintn(Argv[3]);
	Buffer[3]=StrDecimalToUintn(Argv[4]);

	Print(L"Buffer [%x %x %x %x]\n",
			Buffer[0],Buffer[1],Buffer[2],Buffer[3]);		
			
	FileSize=Buffer[0]+
		(Buffer[1]<<8)+
		(Buffer[2]<<16)+
		(Buffer[3]<<24);	

	Print(L"File Size = [%d]\n",FileSize);		
				
  return EFI_SUCCESS;
}

 

运行结果:

tipsa

为了Debug,需要打开生成汇编代码的功能,方法是在工程的 inf 文件 [BuildOptions] 下面加入如下语句:

[BuildOptions]
  MSFT:*_*_IA32_CC_FLAGS  = /FAsc

 

再次编译即可在Build目录下看到生成的 cod 文件

tipsb

关键代码部分:

; 28   : 
; 29   : 	Print(L"Buffer [%x %x %x %x]\n",
; 30   : 			Buffer[0],Buffer[1],Buffer[2],Buffer[3]);		

  00048	0f be 7c 24 3e	 movsx	 edi, BYTE PTR _Buffer$[esp+50]
  0004d	0f be 6c 24 3d	 movsx	 ebp, BYTE PTR _Buffer$[esp+49]
  00052	0f be f0	 movsx	 esi, al
  00055	56		 push	 esi
  00056	57		 push	 edi
  00057	0f be db	 movsx	 ebx, bl
  0005a	55		 push	 ebp
  0005b	53		 push	 ebx
  0005c	68 00 00 00 00	 push	 OFFSET ??_C@_1CM@LIBNEIP@?$AAB?$AAu?$AAf?$AAf?$AAe?$AAr?$AA?5?$AA?$FL?$AA?$CF?$AAx?$AA?5?$AA?$CF?$AAx?$AA?5?$AA?$CF?$AAx?$AA?5?$AA?$CF?$AAx?$AA?$FN?$AA?6?$AA?$AA@
  00061	e8 00 00 00 00	 call	 _Print

 

其中 push esi是 Buffer[3], push edi是 Buffer[2], push ebp 是 Buffer[1] , push ebx 是 Buffer[0],看起来一切正常。我们根据简单的结果进行计算0xFFFFFF80+0x5200+0x00+0x00=0x5180 就是说出现问题出在运行期的取值,和 Printf函数 没有关系。所以,关注点在为什么 1byte的0x80被定义成了 0xFFFFFF80。之后,我尝试化简程序,直接定义变量输出查看。我们定义三个变量 a b c用 printf 输出之:

	CHAR8	a=129;
	UINT8	b=130;
	unsigned char	c=131;

	Print(L"A= [%x]\n",a);					
	Print(L"B= [%x]\n",b);		
	Print(L"C= [%x]\n",c);	

 

运行结果:
tipsc

可以看到,如同我们的假设的, CHAR8 的 128被定义为 -127.
具体的 CHAR8定义可以在 MdePkg 中的 ProcessorBind.h 中找到:

  ///
  /// 1-byte Character.
  ///
  typedef char                CHAR8;

 

CHAR 本身是一种有符号的整数(所以才会有 unsigned char)。因此,这就是一个错误的定义数据类型而导致的问题,解决方法很简单,用 UINT8 做定义就好了(从ProcessorBind.h可以看出,UINT8是 unsigned char)。

最后,贴一下修改后的完整INF 文件以便参考:

## @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.
##

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = tipsa
  FILE_GUID                      = 4ea97c46-7491-4dfd-0064-747010f3ce5f
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = ShellCEntryLib

#   
#  VALID_ARCHITECTURES           = IA32 X64 IPF
#

[Sources]
  tipsa.c

[Packages]
  StdLib/StdLib.dec   
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec 


[LibraryClasses]
  LibC
  ShellCEntryLib   
  ShellLib
  
[Protocols]
	
[BuildOptions]
  MSFT:*_*_IA32_CC_FLAGS  = /FAsc
  

 

Delphi 二维码生成工具 DelphiZXingQRCode

我最近在编写一个二维码生成的代码,使用了 Debenu-DelphiZXingQRCode 这个 VCL ,但是在使用中发现这个工具在编码上存在 bug。

具体来说,我需要对一组十六进制字符串编码(0-9,A-F)但是发现设置为 qrAuto, qrNumeric 以及 qrAlphanumeric 都无法对 0 进行编码。

意思是比如: 55AA000011 生成二维码之后再用手机识别,结果只是 55AA11,中间的 0000 会被删除(我尝试了手机上的2款不同识别内核

的软件,都是这样);如果切换为 qrISO88591 编码之后的结果可以正常识别,但是如果字符串很长效率又低了很多。

暂时不推荐大家使用这个 VCL

DelphiZXingQRCode-master