標籤

顯示具有 語法 標籤的文章。 顯示所有文章
顯示具有 語法 標籤的文章。 顯示所有文章

2014年10月27日 星期一

How to use RTC reigister to set wake up time

From EDKII, It is simple but useful. If your BIOS vendor not provide the items in setup menu, you may give it a try.

1. Check your RTC_EN at PCH.
2. Refer to http://stanislavs.org/helppc/cmos_ram.html, set your alarm time, don't forget to set update enable/disable and AIE bit.

That's all,  EDKII sample code as below

LibSetWakeupTime (
  IN BOOLEAN      Enabled,
  OUT EFI_TIME    *Time
  )
{
  EFI_STATUS            Status;
  EFI_TIME              RtcTime;
  RTC_REGISTER_B        RegisterB;
  UINT8                 Century;
  EFI_TIME_CAPABILITIES Capabilities;

  if (Enabled) {

    if (Time == NULL) {
      return EFI_INVALID_PARAMETER;
    }
    //
    // Make sure that the time fields are valid
    //
    Status = RtcTimeFieldsValid (Time);
    if (EFI_ERROR (Status)) {
      return EFI_INVALID_PARAMETER;
    }
    //
    // Just support set alarm time within 24 hours
    //
    LibGetTime (&RtcTime, &Capabilities);
    if (Time->Year != RtcTime.Year ||
        Time->Month != RtcTime.Month ||
        (Time->Day != RtcTime.Day && Time->Day != (RtcTime.Day + 1))
        ) {
      return EFI_UNSUPPORTED;
    }
    //
    // Make a local copy of the time and date
    //
    CopyMem (&RtcTime, Time, sizeof (EFI_TIME));

  }
  //
  // Acquire RTC Lock to make access to RTC atomic
  //
  EfiAcquireLock (&mRtc.RtcLock);

  //
  // Wait for up to 0.1 seconds for the RTC to be updated
  //
  Status = RtcWaitToUpdate (100000);
  if (EFI_ERROR (Status)) {
    EfiReleaseLock (&mRtc.RtcLock);
    return EFI_DEVICE_ERROR;
  }
  //
  // Read Register B, and inhibit updates of the RTC
  //
  RegisterB.Data      = RtcRead (RTC_ADDRESS_REGISTER_B);

  RegisterB.Bits.SET  = 1;
  RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);

  if (Enabled) {
    ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);

    //
    // Set RTC alarm time
    //
    RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
    RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
    RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);

    RegisterB.Bits.AIE = 1;

  } else {
    RegisterB.Bits.AIE = 0;
  }
  //
  // Allow updates of the RTC registers
  //
  RegisterB.Bits.SET = 0;
  RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);

  //
  // Release RTC Lock.
  //
  EfiReleaseLock (&mRtc.RtcLock);

  return EFI_SUCCESS;
}

2009年7月21日 星期二

存取記憶體 peekb()

  函數名: peekb
  功 能: 檢查存儲單元
  用 法: char peekb (int segment, unsigned offset);
  segment為段基地址,offset偏移地址。
  程序例:

  #include
  #include
  #include
  int main(void)
  {
  int value = 0;
  printf("The current status of your keyboard is:\n");
  value = peekb(0x0040, 0x0017);
  if (value & 1)
  printf("Right shift on\n");
  else
  printf("Right shift off\n");
  if (value & 2)
  printf("Left shift on\n");
  else
  printf("Left shift off\n");
  if (value & 4)
  printf("Control key on\n");
  else
  printf("Control key off\n");
  if (value & 8)
  printf("Alt key on\n");
  else
  printf("Alt key off\n");
  if (value & 16)
  printf("Scroll lock on\n");
  else
  printf("Scroll lock off\n");
  if (value & 32)
  printf("Num lock on\n");
  else
  printf("Num lock off\n");
  if (value & 64)
  printf("Caps lock on\n");
  else
  printf("Caps lock off\n");
  return 0;
  }


http://baike.baidu.com/view/1983818.html?fromTaglist

2009年7月20日 星期一

取得檔案時間 "findfirst" (struct ffblk)

http://www.delorie.com/djgpp/doc/libc/libc_326.html

findfirst

Syntax

#include

int findfirst(const char *pathname, struct ffblk *ffblk, int attrib);

Description

This function and the related findnext (see section findnext) are used to scan directories for the list of files therein. The pathname is a wildcard that specifies the directory and files to search for (such as subdir/*.c), ffblk is a structure to hold the results and state of the search, and attrib is a combination of the following:

FA_RDONLY

Include read-only files in the search (Ignored.)

FA_HIDDEN

Include hidden files in the search

FA_SYSTEM

Include system files in the search

FA_LABEL

Include the volume label in the search

FA_DIREC

Include subdirectories in the search

FA_ARCH

Include modified files in the search (Ignored.)

If a file has flag bits that are not specified in the attrib parameter, the file will be excluded from the results. Thus, if you specified FA_DIREC and FA_LABEL, subdirectories and the volume label will be included in the results. Hidden and system files will be excluded.

Since findfirst calls DOS function 4eh, it is not possible to exclude read-only files or archive files from the results. Even if the FA_ARCH and FA_RDONLY bits are not specified in the attrib parameter, the results will include any read-only and archive files in the directory searched.

This function supports long file names.

The results of the search are stored in ffblk, which is extended when the LFN API (see section LFN) is supported. Fields marked LFN are only valid if the lfn_magic member is set to "LFN32".



struct ffblk {
char lfn_magic[6]; /* LFN: the magic "LFN32" signature */
short lfn_handle; /* LFN: the handle used by findfirst/findnext */
unsigned short lfn_ctime; /* LFN: file creation time */
unsigned short lfn_cdate; /* LFN: file creation date */
unsigned short lfn_atime; /* LFN: file last access time (usually 0) */
unsigned short lfn_adate; /* LFN: file last access date */
char ff_reserved[5]; /* used to hold the state of the search */
unsigned char ff_attrib; /* actual attributes of the file found */
unsigned short ff_ftime; /* hours:5, minutes:6, (seconds/2):5 */
unsigned short ff_fdate; /* (year-1980):7, month:4, day:5 */
unsigned long ff_fsize; /* size of file */
char ff_name[260]; /* name of file as ASCIIZ string */
}


Return Value

Zero if a match is found, nonzero if none found.

Portability

ANSI/ISO C No
POSIX No

Example



struct ffblk f;
int done = findfirst("*.exe", &f, FA_HIDDEN | FA_SYSTEM);
while (!done)
{
printf("%10u %2u:%02u:%02u %2u/%02u/%4u %s\n",
f.ff_fsize,
(f.ff_ftime >> 11) & 0x1f,
(f.ff_ftime >> 5) & 0x3f,
(f.ff_ftime & 0x1f) * 2,
(f.ff_fdate >> 5) & 0x0f,
(f.ff_fdate & 0x1f),
((f.ff_fdate >> 9) & 0x7f) + 1980,
f.ff_name);
done = findnext(&f);
}

2009年7月17日 星期五

fread() 遇到1Ah的問題

QUESTION:
如果fopen不用二進位方式打開,如:fopen(filename,"r"),只要檔案有一位元組為1Ah,fread函數會停止讀取。

ANS:

用fopen(filename,"rb")

註:讀取任意的png圖片,如果fopen採用"r"方式,則fread只能讀取5個位元組,因為png格式的圖片第六位元為1A。

2009年6月10日 星期三

assembly 語法筆記

lods
從DS:SI所指向的空間中 取出一個byte/word/dword 放入 AL/AX/EAX 同時把SI+(-)1/2/4

lods相當於

mov AL, BYTE PTR ds:[si]
inc si


lods DWORD PTR es:[esi]相當於

mov eax, DWORD PTR es:[esi]
add esi, 4


align PTR
http://evotalk.net/blog/?p=116
OFFSET : 傳回變數從所在區段開始的偏移距離

.data
bVal BYTE ?
wVal WORD ?
dVal DWORD ?
dVal2 DWORD ?
.code ; assume bVal's offset : 00404000
mov esi , OFFSET bVal ; ESI = 00404000
mov esi , OFFSET wVal ; ESI = 00404001
mov esi , OFFSET dVal ; ESI = 00404003
mov esi , OFFSET dVal2 ; ESI = 00404007


ALIGN : 將變數置於位元組、字組、雙字組或是段落的邊界
ALIGN 邊界
邊界 = 1 : 下一變數位址會置於一個位元組的邊界(預設)
邊界 = 2 : 下一變數位址會置於一個雙數的位址的邊界
邊界 = 4 : 下一變數位址會置於一個四的倍數位址的邊界

bval BYTE ? ; 00404000
ALIGN 2
wVal DWORD ? ; 00404002
bVal2 BYTE ? ; 00404004
ALIGN 4
dVal DWORD ? ; 原本是00404005,現為00404008
dVal2 DWORD ? ; 0040400C


PTR : 存取的變數大小與當初宣告的變數不同

.data
myDouble DWORD 12345678h
.code mov ax , myDouble ; error
mov ax , WORD PTR myDouble ; ax = 5678h
mov ax , WORD PTR [myDouble + 2] ; ax = 1234h
mov bl , BYTE PTR myDouble ; bl = 78h


TYPE : 傳回變數單一元件以 byte為單位的大小值

.data
var1 BYTE ?
var2 WORD ?
var3 DWORD ?
var4 QWORD ?
TYPE var1 ; 1
TYPE var2 ; 2
TYPE var3 : 4
TYPE var4 ; 8


LENGTHOF : 計算陣列中的元件數

.data
byte1 BYTE 10,20,30
array1 WORD 30 DUP(?) , 0,0
array2 WORD 5 DUP( 3 DUP(?) )
array3 DWORD 1 ,2 ,3, 4
digitStr BYTE "12345678" , 0
LENGTHOF byte1 ; 3
LENGTHOF array1 ; 30+2
LENGTHOF array2 ; 5*3
LENGTHOF array3 ; 4
LENGTHOF digitStr ; 9
myArray BYTE 10,20,30,40,50
BYTE 60,70,80,90,100
LENGTHOF myArray ;只計算第一列定義的部分陣列,傳回 5


SIZEOF : 傳回的值等於LENGTHOF乘上TYPE的值

intArray WORD 32 DUP(0) ; SIZEOF = 32 * 2

CPUID詳解

標 題: CPUID詳解[增加TLB與Cache]
作 者: Pr0Zel
時 間: 2006-02-21 22:00
鏈 接: http://bbs.pediy.com/showthread.php?threadid=21646
詳細信息:

這是文章最後一次更新,加入了TLB與Cache信息等資料
前言:論壇上面有人不明白CPUID指令的用法,於是就萌生寫這篇文章的想法,
若有錯誤話請大俠指出,謝謝了 ^^
論壇的式樣貌似有問題,若式樣問題導致閱讀問題的話,可以在文章尾下載txt文檔閱讀.

論壇上面有人不明白CPUID指令的用法,於是就萌生寫這篇文章的想法,
若有錯誤話請大俠指出,謝謝了 ^^
CPUID是Intel Pentium以上級CPU內置的一個指令(486級及以下的CPU不支持),它用於識別某一類型的CPU,它能返回CPU的級 別(family),型號(model),CPU步進(Stepping ID)及CPU字串等信息,從此命令也可以得到CPU的緩存與TLB信息.
CPUID返回數據類型是在EAX寄存器裡面定義的,而指令返回的數值則在存儲在EAX,EBX,ECX和EDX寄存器裡面.
返回的信息分兩部分:基本信息與擴展信息.在EAX輸入0-3參數時,它返回的CPU的基本信息;而在EAX輸入0x8000000至0x800000x 時,它返回的是CPU的擴展信息(extended function information).擴展信息只包含在Pentium 4及以後的CPU 上,Pentium 4以前的CPU無法取得它的擴展信息.

如下面的表:
CPU級別 基本信息 擴展信息
486及以前的CPU 不可用 不可用
Pentium 0x1 不可用
Pentium Pro,Pentium 2 0x2 不可用
Pentium 3 0x3 不可用
Pentium 4 0x2 0x80000004
Xeon(至強) 0x2 0x80000004

假若輸入高於該處理器的值時,CPUID指令返回的是該CPU的輸入最高值的返回值(這一句不知道怎麼說才好),
比如在在Pentium 4上輸入0x4,則CPU返回值與輸入0x2的返回值一樣.

下面的表是輸入值與返回值的關係:
輸入值 返回值
-----------------------------------------------------------------
0x0 EAX CPU基本參數的輸入值
EBX "Genu"
ECX "Intel"
EDX "inel"
------------------------------------------------------------------
0x1 EAX CPU的級別,型號及步進
EBX 信息很多,下面介紹
ECX 保留
EDX 特徵信息(Feature Information)
------------------------------------------------------------------
0x2 EAX到EDX返回的都是緩存和TLB的信息
------------------------------------------------------------------
0x3 EAX 保留
EBX 保留
ECX CPU序列號(0 - 31bit) (只是在Pentium 3中才有效)
EDX CPU序列號(32 - 63bit) (只是在Pentium 3 中才有效)
------------------------------------------------------------------

0x80000000 EAX 擴展信息輸入數最大值(具有擴展信息的CPU才能返回)
EBX 保留
ECX 保留
EDX 保留
------------------------------------------------------------------
0x80000001 EAX CPU特徵(Signature)和擴展特徵位(Extended Feature Bits)
EBX 到 ECX 保留
------------------------------------------------------------------
0x80000002 EAX 處理器字串(Processor Brand String)
EBX 處理器字串(續)
ECX 處理器字串(續)
EDX 處理器字串(續)
------------------------------------------------------------------
0x80000003 EAX 處理器字串(續)
EBX 處理器字串(續)
ECX 處理器字串(續)
EDX 處理器字串(續)
------------------------------------------------------------------
0x80000004 EAX 處理器字串(續)
EBX 處理器字串(續)
ECX 處理器字串(續)
EDX 處理器字串(續)
------------------------------------------------------------------

當輸入0x1時,EBX返回值是:
第 0 - 7位: CPU字串索引 (Brand Index)
第 8 - 15位: CLFLUSH線大小(CLFLUSH line size) (返回值*8 = cache line size)
第16 - 23位: 保留
第24 - 31位: 處理器APIC物理標號 (Processor local APIC physical ID)

當輸入0x1時,EDX返回的擴展信息解釋如下:

位 標號 解釋
0 FPU Floating Point Unit On-Chip. CPU是否內置浮點計算單元
1 VME Virtual 8086 Mode Enhancements. 是否支持虛擬8086模式
2 DE Debugging Extensions. 是否支持調試功能.
3 PSE Page Size Extension. 是否支持大於4MB的分頁.
4 TSC Time Stamp Counter. 是否支持RDTSC指令.(注:RDTSC指令可以計算出CPU的頻率)
5 MSR Module Specific Registers RDMSR and WRMSR Instructions. 是否支持RDMSR與WRMSR (*注1)
6 PAE Physical Address Extension. 是否支持大於32bit的物理地址.
7 MCE Machine Check Exception. (*注2)
8 CX8 CMPXCHG8B Instruction. 是否支持8bytes(64bit)數的比較與交換指令.
9 APIC APIC On-Chip.是否支持APIC(Advanced Programmable Interrupt Controller)
10 保留
11 SEP SYSENTER and SYSEXIT Instructions.是否支持SYSENTER與SYSEXIT指令.(*注3)
12 MTRR Memory Type Range Registers. 是否支持MTTR(*注4)
13 PGE PTE Global Bit. 是否支持全局頁面目錄入口標誌位 (global bit in page directory entries)
14 MCA Machine Check Architecture. 是否支持MCA,MCA是Pentium4,Xeon,P6級處理器的一個錯誤報告機制
15 CMOV Conditional Move Instructions. CMOV指令是否可用.(請問誰可以解釋一下CMOV是什麼命令?)
16 PAT Page Attribute Table. 是否支持PAT,PAT允許操作系統指定4K大小的線性內存空間
17 PSE-36 32-bit Page Size Extension. 是否支持4GB的擴展內存
18 PSN Processor Serial Number. 是否支持處理器序列號.(P3有效)
19 CLFSH CLFLUSH Instruction.是否支持CLFLUSH.(*注5)
20 保留
21 DS Debug Store. 是否支持把調試信息寫入緩存,
22 ACPI ACPI Processor Performance Modulation Registers. 處理器使用特別的寄存器以允許軟件控制處理器的運行週期.
23 MMX Inter MMX Technology.是否支持MMX
24 FXSR FXSAVE and FXRSTOR Instructions. FXSAVE與FXRSTOR指令是否可用(*注6)
25 SSE SSE.是否支持SSE.
26 SSE2 是否支持SSE2.
27 SS Self Snoop. 處理器是否支持總線監視,以防止儲存器衝突.
28 保留
29 TM Thermal Monitor.CPU是否支持溫度控制.
30 & 31 保留
--------------------------------------------------------------
注1: RDMSR: Load MSR specified by ECX into EDX:EAX
WRMSR: Write the value in EDX:EAX to MSR specified by ECX
注2:
原文是 Exception 18 is defined for Machine Checks,including CR4.MCE for controlling the feature. This feature does not define themodel-specific implementations of machine-check error logging, reporting, andprocessor shutdowns. Machine Check exception handlers may have to depend onprocessor version to do model specific processing of the exception, or test for thepresence of the Machine Check feature.

注3: SYSENTER: Fast call to privilege level 0 system procedures
SYSEXIT: Fast return to privilege level 3 user code.

注4:
原文是 The MTRRcap MSR contains feature bits that describe what memory types are supported, how manyvariable MTRRs are supported, and whether fixed MTRRs are supported.

注5: CLFLUSH: Flushes cache line containing m8.

注6: FXSAVE: Save the x87 FPU, MMX, XMM, and MXCSR register
FXSTOR: Restore the x87 FPU, MMX, XMM, and MXCSR register


按照這個,就可以自己寫一個CPU檢測程序了;


#include

void main()
{
unsigned long DBaseIndex, DFeInfo, DFeInfo2, DCPUBaseInfo;
unsigned long DFeIndex, DCPUExInfo, i;
unsigned long DOther[4], DTLB[4], DProceSN[2];
char cCom[13];
char cProStr[49];
unsigned int j;

_asm
{
xor eax, eax
cpuid
mov DBaseIndex ,eax
mov dword ptr cCom ,ebx
mov dword ptr cCom+4 ,ecx //AMD CPU要把ecx改為edx
mov dword ptr cCom+8 ,edx //AMD CPU要把edx改為ecx

mov eax, 1
cpuid
mov DCPUBaseInfo, eax
mov DFeInfo, ebx
mov DFeInfo2, edx

mov eax, 0x80000000
cpuid
mov DFeIndex, eax

mov eax, 0x80000001
cpuid
mov DCPUExInfo, eax

mov eax, 0x80000002
cpuid
mov dword ptr cProStr , eax
mov dword ptr cProStr + 4 , ebx
mov dword ptr cProStr + 8 , ecx
mov dword ptr cProStr + 12 ,edx

mov eax, 0x80000003
cpuid
mov dword ptr cProStr + 16 , eax
mov dword ptr cProStr + 20 , ebx
mov dword ptr cProStr + 24 , ecx
mov dword ptr cProStr + 28 , edx

mov eax, 0x80000004
cpuid
mov dword ptr cProStr + 32 , eax
mov dword ptr cProStr + 36 , ebx
mov dword ptr cProStr + 40 , ecx
mov dword ptr cProStr + 44 , edx
}

if( DBaseIndex >= 2 )
{
_asm
{
mov eax, 2
cpuid
mov DTLB[0], eax
mov DTLB[2], ebx
mov DTLB[3], ecx
mov DTLB[4], edx
}
}
if(DBaseIndex == 3)
{
_asm
{
mov eax, 3
cpuid
mov DProceSN[0], ecx
mov DProceSN[1], edx
}
}

cCom[12] = '\0'; //加一個結尾符
printf( "CPU廠商: %s\n", cCom );
printf( "CPU字串: %s\n", cProStr );
printf( "CPU基本參數: Family:%X Model:%X Stepping ID:%X\n", (DCPUBaseInfo & 0x0F00) >> 8,
(DCPUBaseInfo & 0xF0) >> 4, DCPUBaseInfo & 0xF );
printf( "CPU擴展參數: Family:%X Model:%X Stepping ID:%X\n", (DCPUExInfo & 0x0F00) >> 8,
(DCPUExInfo & 0xF0) >> 4, DCPUExInfo & 0xF );

printf( "CPU字串索引: 0x%X\n", DFeInfo & 0xFF );
printf( "CLFLUSH線大小: 0x%X\n", ( DFeInfo & 0xFF00 ) >> 8 );
printf ( "處理器APIC物理標號:0x%X\n", ( DFeInfo & 0xF000 ) >> 24 );
if( DBaseIndex >= 2)
{
printf( "CPU Cache & TLB Information: " );
for(j = 0; j <>> 8) ) printf( "%.2X ", (DTLB[j] & 0xFF00) >> 8 );
if( !((DTLB[j] & 0xFF0000) >> 16) ) printf( "%.2X ",( DTLB[j] & 0xFF0000) >> 16);
if( !((DTLB[j] & 0xFF000000) >> 24) ) printf( "%.2X ",( DTLB[j] & 0xFF000000) >> 24);
}
printf("\n");
}

if( DBaseIndex == 3 )
{
printf( "CPU序列號是:%X%X\n", DProceSN[0], DProceSN[1] );
}
printf( "FPU: %d\t\t", DFeInfo2 & 0x00000001 ); //下面是調用某BLOG上面的代碼,懶得寫了 ^^
printf( "VME: %d\t\t", (DFeInfo2 & 0x00000002 ) >> 1 );
printf( "DE: %d\n", (DFeInfo2 & 0x00000004 ) >> 2 );
printf( "PSE: %d\t\t", (DFeInfo2 & 0x00000008 ) >> 3 );
printf( "TSC: %d\t\t", (DFeInfo2 & 0x00000010 ) >> 4 );
printf( "MSR: %d\n", (DFeInfo2 & 0x00000020 ) >> 5 );
printf( "PAE: %d\t\t", (DFeInfo2 & 0x00000040 ) >> 6 );
printf( "MCE: %d\t\t", (DFeInfo2 & 0x00000080 ) >> 7 );
printf( "CX8: %d\n", (DFeInfo2 & 0x00000100 ) >> 8 );
printf( "APIC: %d\t", (DFeInfo2 & 0x00000200 ) >> 9 );
printf( "SEP: %d\t\t", (DFeInfo2 & 0x00000800 ) >> 11 );
printf( "MTRR: %d\n", (DFeInfo2 & 0x00001000 ) >> 12 );
printf( "PGE: %d\t\t", (DFeInfo2 & 0x00002000 ) >> 13 );
printf( "MCA: %d\t\t", (DFeInfo2 & 0x00004000 ) >> 14 );
printf( "CMOV: %d\n", (DFeInfo2 & 0x00008000 ) >> 15 );
printf( "PAT: %d\t\t", (DFeInfo2 & 0x00010000 ) >> 16 );
printf( "PSE-36: %d\t", (DFeInfo2 & 0x00020000 ) >> 17 );
printf( "PSN: %d\n", (DFeInfo2 & 0x00040000 ) >> 18 );
printf( "CLFSN: %d\t", (DFeInfo2 & 0x00080000 ) >> 19 );
printf( "DS: %d\t\t", (DFeInfo2 & 0x00200000 ) >> 21 );
printf( "ACPI: %d\n", (DFeInfo2 & 0x00400000 ) >> 22 );
printf( "MMX: %d\t\t", (DFeInfo2 & 0x00800000 ) >> 23 );
printf( "FXSR: %d\t", (DFeInfo2 & 0x01000000 ) >> 24 );
printf( "SSE: %d\n", (DFeInfo2 & 0x02000000 ) >> 25 );
printf( "SSE2: %d\t", (DFeInfo2 & 0x04000000 ) >> 26 );
printf( "SS: %d\t\t", (DFeInfo2 & 0x08000000 ) >> 27 );
printf( "TM: %d\n", (DFeInfo2 & 0x20000000 ) >> 29 );

printf("\n其它信息:\n");
printf("----------------------------------------\n");
printf("In \t\tEAX \t\tEBX \t\tECX \t\tEDX");
for( i = 0x80000004; i <= DFeIndex; ++i ) { DOther[0] = DOther[1] = DOther[2] = DOther[3] = 0; _asm { mov eax, i cpuid mov DOther[0], eax mov DOther[1], ebx mov DOther[2], ecx mov DOther[3], edx } printf( "\n0x%.8X\t0x%.8X\t0x%.8X\t0x%.8X\t0x%.8X", i, DOther[0], DOther[1], DOther[2], DOther[3] ); } printf( "\n" ); system( "pause" ); }


TLB與Cache信息詳解:
0x00 Null descriptor
0x01 Instruction TLB: 4K-Byte Pages, 4-way set associative, 32 entries 0x02 Instruction TLB: 4M-Byte Pages, 4-way set associative, 2 entries
0x03 Data TLB: 4K-Byte Pages, 4-way set associative, 64 entries
0x04 Data TLB: 4M-Byte Pages, 4-way set associative, 8 entries
0x06 1st-level instruction cache: 8K Bytes, 4-way set associative, 32 byte line size
0x08 1st-level instruction cache: 16K Bytes, 4-way set associative, 32 byte line size
0x0A 1st-level data cache: 8K Bytes, 2-way set associative, 32 byte line size
0x0C 1st-level data cache: 16K Bytes, 4-way set associative, 32 byte line size 0x22 3rd-level cache: 512K Bytes, 4-way set associative, 64 byte line size
0x23 3rd-level cache: 1M Bytes, 8-way set associative, 64 byte line size
0x25 3rd-level cache: 2M Bytes, 8-way set associative, 64 byte line size
0x29 3rd-level cache: 4M Bytes, 8-way set associative, 64 byte line size
0x40 No 2nd-level cache or, if processor contains a valid 2nd-level cache, no 3rd-level cache
0x41 2nd-level cache: 128K Bytes, 4-way set associative, 32 byte line size
0x42 2nd-level cache: 256K Bytes, 4-way set associative, 32 byte line size
0x43 2nd-level cache: 512K Bytes, 4-way set associative, 32 byte line size
0x44 2nd-level cache: 1M Byte, 4-way set associative, 32 byte line size
0x45 2nd-level cache: 2M Byte, 4-way set associative, 32 byte line size
0x50 Instruction TLB: 4-KByte and 2-MByte or 4-MByte pages, 64 entries
0x51 Instruction TLB: 4-KByte and 2-MByte or 4-MByte pages, 128 entries
0x52 Instruction TLB: 4-KByte and 2-MByte or 4-MByte pages, 256 entries
0x5B Data TLB: 4-KByte and 4-MByte pages, 64 entries
0x5C Data TLB: 4-KByte and 4-MByte pages,128 entries
0x5D Data TLB: 4-KByte and 4-MByte pages,256 entries
0x66 1st-level data cache: 8KB, 4-way set associative, 64 byte line size
0x67 1st-level data cache: 16KB, 4-way set associative, 64 byte line size
0x68 1st-level data cache: 32KB, 4-way set associative, 64 byte line size
0x70 Trace cache: 12K-μop, 8-way set associative
0x71 Trace cache: 16K-μop, 8-way set associative
0x72 Trace cache: 32K-μop, 8-way set associative
0x79 2nd-level cache: 128KB, 8-way set associative, sectored, 64 byte line size 0x7A 2nd-level cache: 256KB, 8-way set associative, sectored, 64 byte line size 0x7B 2nd-level cache: 512KB, 8-way set associative, sectored, 64 byte line size 0x7C 2nd-level cache: 1MB, 8-way set associative, sectored, 64 byte line size
0x82 2nd-level cache: 256K Bytes, 8-way set associative, 32 byte line size
0x84 2nd-level cache: 1M Byte, 8-way set associative, 32 byte line size
0x85 2nd-level cache: 2M Byte, 8-way set associative, 32 byte line size

舉一個例子,一個CPU執行 mov eax, 2 cpuid 後, 返回值如下:
EAX 0x665B5001 EBX 0x0 ECX 0x0 EDX 0x007A7000

就代表了這個CPU的Cache信息是:
1. 0x66:1st-level data cache: 8KB, 4-way set associative, 64 byte line size
2. 0x5B:Data TLB: 4-KByte and 4-MByte pages, 64 entries
3. 0x50:Instruction TLB: 4-KByte and 2-MByte or 4-MByte pages, 64 entries
4. 0x01:Instruction TLB: 4K-Byte Pages, 4-way set associative, 32 entries
5. 0x7A:2nd-level cache: 256KB, 8-way set associative, sectored, 64 byte line size 6. 0x70:Trace cache: 12K-μop, 8-way set associative

CPU字串索引 (Brand Index)詳解:
0x0: CPU不支持Brand Index
0x1: Celeron CPU
0x2: Pentium 3
0x3: Pentium 3 Xeon
0x4-0x7: 未用
0x8: Pentium 4 可惜我是用AMD CPU的,有好些功能都不能親自試驗
參考資料:IA-32 Intel Architecture Software Developer's Manual

2009年4月29日 星期三

Easy DOS command

http://www.slime.com.tw/teach/neto/teach/dos.htm

Easy DOS command
前言: 剛開始接觸電腦的人, 因為 Windows 的好用, 會讓人覺得學習電腦實在很容易, 可是如果要到 DOS 執行的指令, 總是令人感到蠻害怕的, 畢竟 DOS 給人的感覺沒有 Windows 來的親切嘛! 但是站長要跟你說喔! DOS 的指令其實也只有那麼一丁點 , 只要這點小指令你學起來了, 管它是 DOS 還是 Windows , 都是通行無阻的喔! 從此之後 , 保證 DOS 不再讓你害怕了, 讓我來教教你這些簡單的指令吧!
( 雖然 MS 的新產品將不再支援 DOS 系統 , 不過 MS 的 NT 裡也是有類似 DOS 的工具存在 , 所以這些指令對初學者也不會完全沒用。)
DOS 2000 年危機 :
1. 在 DOS 下 DIR 列出的檔案為什麼日期顯示為 00 呢 ? 別擔心 , 只要加上 /4 參數就可以將日期正常顯示為 2000 了 ,
ex. dir/4如果您嫌每次 DIR 都要加上 /4 參數粉麻煩的話 , 您也可以在 C:\Config.sys 裡增加這一行 set dircmd=/4 , 你可以用筆記本來開啟Config.sys 這個檔案 , 等修改好了存檔完畢之後重新開機就可以啦! 以後 DOS 下顯示的日期就是 2000 了。

 
1.DIR: 如果你在某個目錄下, 這個所謂的目錄可以是磁碟機, 也可以是資料夾, 譬如 C:\ 好了, 那你想看看 C: 槽究竟放了哪些資料呢? 很簡單喔! 你只要在 C:\ 的後面打上 DIR 就好了, 是不是粉簡單呢? 還有喔! 如果你覺得: 打個 DIR 在按 Enter 之後, 那個檔案咻就跑到最下面去了, 根本都沒看清楚就給它跑走了, 站長跟你說, 沒關係的, 我們不要跟它計較跑的那麼快, 否則有損身為人的自尊哦! 只要在打 DIR/P ,YA! 它每次都只會出現一頁的部分, 等我們看清楚了在按 Enter 看下一頁不就好啦! OK! 還有一個秘密喔! 就是如果你打 DIR/W 的話, 它只會顯示檔案名稱, 不會顯示一堆日期, 檔案大小, ...一堆有的沒有的。
2.FORMAT: 這個指令的用途就是格式化, 你應該知道格式化是什麼吧! 就是可以消滅磁碟中的病毒, 資料,裡面所有的東西, 很可怕喔! 不小心資料都會毀了, 但是磁片或是硬碟在剛買來都是要格式化過的, (現在買到的磁片大多數好像都已經格式化過了 ) ,其實如果磁片壞軌了, 也可以重新格式化, 有時候就好了說, 言歸正傳, 如果我們要格式化 A 槽, 只要打 FORMAT A: 就可以了, 格式化 C 槽就打入 FORMAT C: 就可以了。
3.COPY: 這個指令一看也知道是複製檔案用的嘛! 如果我們要將 A: 中的 lmi.txt 這個檔案複製到 C: 則可以打
COPY A: lmi.txt C:

如果我們要把 lmi.txt 從 A: 複製到 C: 而且要把複製的檔案名稱更為 hsuyin.txt 的話, 我們可以這樣作

COPY A: lmi.txt C: hsuyin.txt

最後還有一個絕招要跟你介紹, 如果你想要複製 A: 中所有的 .EXE檔到 C: ,我們就可以打
COPY A: *.EXE C: 就可以了。

4.DISCOPY: 這跟 COPY 很像, 但不太一樣, 如果你要把 A: 磁片內容整個複製到 B: 你可以打 DISCOPY A: B:
5. XCOPY : 這個指令可以用來複製整個目錄 , 譬如我要將 d:\HSUYIN 這個目錄裡的檔案全部複製到 d:\LMI 裡面 , 只要打入 xcopy d:\HSUYIN d:\LMI 就可以啦 。
6.DEL: 這個指令可以刪除檔案的, 如果說你的電腦中了病毒, 或者你想要刪除一些檔案, 可是在 Windows 系統中它總是告訴你 "Windows 正在使用檔案, 因此無法刪除", 那就是要到 DOS 刪除了, 假設我要刪除 lmi.txt 我只要打 DEL lmi.txt 就可以了, 那如果你要刪除資料夾中所有檔案只要打 DEL *.* 就可以啦。
7.REN: 這是將檔案重新命名, 例如我要將 LMI.TXT 改成 HSUYIN.TXT 就可以打 REN LMI.TXT HSUYIN.TXT 當然, 如果你要更改所有的副檔名, 你也可以打 REN *.TXT *.DOC ,清楚這指令的意思嗎? 這是說我把這個資料夾中所有的 .txt 改成 .doc 檔。
8.MD: 這個指令可以建立一個新的資料夾, 如果我要建立一個 hsuyin 的資料夾我就可以打入 MD hsuyin 或是打 MKDIR hsuyin 也是可以的。
9.RD: 這個指令跟 MD 恰好相反, 可以刪除一個空的資料夾, 一定要是空的才有辦法刪除喔! 用法是這樣子的, 假設我要刪除 hsuyin 就可以打 RD hsuyin 或是打 RMDIR hsuyin
10. DELTREE: 這是比 RD 還要強的刪除資料夾指令, 因為如果要刪除一個資料夾 RD 就要先刪除裡頭所有的資料夾跟檔案, 而 DELTREE 可以連資料夾裡面的資料夾或是檔案一起刪除, DELTREE 就跟 Windows 中的刪除資料夾一樣把整個資料夾給刪除了, 我要刪除 hsuyin 這個資料夾就可以打 DELTREE hsuyin 就可以啦, 是不是粉簡單呢 ?
11.TYPE: 這個指令是給我們列印文字檔用的, 假設我要印 LMI.txt 的內容就打 TYPE LMI.txt 就可以了。
12.CLS: 用來清除螢幕。
13. 尋找檔案 : 在 DOS 裡尋找檔案也蠻方便的喔 ! 如果你要在 C:\ 找一個檔名為 XXX.txt 的檔案你可以打 dir /s XXX.txt 這樣就可以搜尋出所有 c:\ 中的 XXX.txt
14. cd: 在 Windows 中我們要進入一個資料夾只要用滑鼠左鍵點兩下就好啦 ! 在 DOS 中要進入資料夾就是打 CD 了, 現在假設現在我停留在 c:\ 裡 , 我要進入 C:\ 中的 Windows 資料夾 , 我只要打 cd Windows 就可以了, 如果你現在是在 A: ,你只要打 C: 就可以進入 C: 了, 那要怎麼離開資料夾呢? 如果我是在 C:\Windows\Desktop 而我要退回到 C:\Windows 我只要打 cd.. 就可以了。
15. 離開 dos : 若您開啟的是 windows 下的 dos , 那麼要離開 dos 程式只要鍵入 EXIT 就可以囉!
最後我在說明一下這些符號 ( 萬用字元 ) 的意思: 就是 * 跟 ? ( * 是用來當作任意一行字 ? 則是用來當作任意一個字來用), 就像我們之前說的, 我要複製所有的 .txt 檔到 A: 我們可以打 COPY *.txt A: 我們要刪除所有檔案可以打 DEL *.* ( * 就像是一個數學中的變數 X 在這裡我們讓 * 代表了任意一行字, *.txt 就是所有的 .txt 檔, 不管它叫作 lmi.txt 或是hsuyin.txt 或是 123.txt 或是 abcd.txt 都可以 , *.txt 中的 * 取代了 .txt 之前的那串字, 所以不管是 lmi 或是 hsuyin 或是 123 或是 abcd 都可以用 * 來表示, 這樣對 * 的意思你們應該已經瞭解了吧!嗯! 接下來說 ? 的意思, 其實 * 只要懂了, ? 的意思也不難, 因為 * 是代表一串字, 而 ? 是代表一個字, 喔! 知道了吧! 你看看喔! DEL ???.txt 是什麼意思, 我們是要刪除所有在 .txt 前面是三個字的檔案, 以前面的例子來說, lmi.txt 跟 123.txt 會被刪除, 而 hsuyin.txt 和 abcd.txt 因為 hsuyin 跟 abcd 不是 3 個字元嘛! 所以不會被刪除, 蠻好玩的喔!
其他指令: ( 可以不用看 , 不過您可以試試看 , 蠻有趣的 )
1. date : 更改系統日期 。
2. time : 更改系統時間 。
3. ver : 查詢您系統的版本 。
4. edit : DOS 下的文書編輯程式 。
5. more : 與 type 一樣功能 , 不過有自動分頁 。
6. move : 移動檔案 , 用法與 copy 類似 。
7." > " : 改變輸出 , 譬如 dir > 1.txt 將會把 dir 出來的內容傳到 1.txt 這個文字檔案裡面 ( 1.txt 可命名為任意文字檔名 , 1.txt 可為已存在檔案或是不存在的檔案 , 若為不存在檔案 , 系統會自動建立。 )
8. 指令參數使用說明 : 以上的 dos 指令都有更多詳細的參數使用說明 , 您可以在指令後加上 /? 就可以查詢此只應的參數了 , 比如我要查詢 dir 的用法只要在 dos 中鍵入 dir/? 即可。
9. attrib 在 DOS 底下更改檔案屬性 : 比如我要將 readme.txt 屬性改成隱藏檔 , 可以打 attrib +h readme.txt , 那如果要取消檔案隱藏的屬性 , 只要將 + 改成 - 號就可以了 ex. attrib -h readme.txt , 更多的參數設定請在 DOS 底下打入 attrib /? 查詢。
如果對更多 DOS 的指令有興趣 , 您可以查詢關於 DOS 的書籍。
 
 
以上資料僅供參考
lmi 撰寫

2009年4月2日 星期四

MASM note: asume

Since the ds register can be changed at run time (using an instruction like mov ds,ax), any segment can be a data segment.

When you specify a segment in your program, not only must you tell the CPU that a segment is a data segment, but you must also tell the assembler where and when that segment is a data (or code/stack/extra/F/G) segment.

Note that this directive does not modify any of the segment registers, it simply tells the assembler to assume the segment registers are pointing at certain segments.

assume directive modifies the assembler's behavior from the point MASM encounters it until another assume directive changes the stated assumption.


DSEG1 segment para public 'DATA'
var1 word ?
DSEG1 ends

DSEG2 segment para public 'DATA'
var2 word ?
DSEG2 ends

CSEG segment para public 'CODE'
assume CS:CSEG, DS:DSEG1, ES:DSEG2
mov ax, seg DSEG1
mov ds, ax
mov ax, seg DSEG2
mov es, ax

mov var1, 0
mov var2, 0
.
.
.
assume DS:DSEG2
mov ax, seg DSEG2
mov ds, ax
mov var2, 0
.
.
.
CSEG ends
end


The 80x86 microprocessor doesn't know about segments declared within your program, it can only access data in segments pointed at by the cs, ds, es, ss, fs.

When the assembler encounters an instruction of the form mov var1,0, the first thing it does is determine var1's segment. It then compares this segment against the list of assumptions the assembler makes for the segment registers. If you didn't declare var1 in one of these segments, then the assembler generates an error claiming that the program cannot access that variable. If the symbol (var1 in our example) appears in one of the currently assumed segments, then the assembler checks to see if it is the data segment. If so, then the instruction is assembled as described in the appendices. If the symbol appears in a segment other than the one that the assembler assumes ds points at, then the assembler emits a segment override prefix byte, specifying the actual segment that contains the data.

In the example program above, MASM would assemble mov VAR1,0 without a segment prefix byte. MASM would assemble the first occurrence of the mov VAR2,0 instruction with an es: segment prefix byte since the assembler assumes es, rather than ds, is pointing at segment DSEG2. MASM would assemble the second occurrence of this instruction without the es: segment prefix byte since the assembler, at that point in the source file, assumes that ds points at DSEG2. Keep in mind that it is very easy to confuse the assembler.


CSEG segment para public 'CODE'
assume CS:CSEG, DS:DSEG1, ES:DSEG2
mov ax, seg DSEG1
mov ds, ax
.
.
.
jmp SkipFixDS

assume DS:DSEG2

FixDS: mov ax, seg DSEG2
mov ds, ax
SkipFixDS:
.
.
.
CSEG ends
end


Notice that this program jumps around the code that loads the ds register with the segment value for DSEG2. This means that at label SkipFixDS the ds register contains a pointer to DSEG1, not DSEG2. However, the assembler isn't bright enough to realize this problem, so it blindly assumes that ds points at DSEG2 rather than DSEG1. This is a disaster waiting to happen. Because the assembler assumes you're accessing variables in DSEG2 while the ds register actually points at DSEG1, such accesses will reference memory locations in DSEG1 at the same offset as the variables accessed in DSEG2. This will scramble the data in DSEG1 (or cause your program to read incorrect values for the variables assumed to be in segment DSEG2).

For beginning programmers, the best solution to the problem is to avoid using multiple (data) segments within your programs as much as possible. Save the multiple segment accesses for the day when you're prepared to deal with problems like this. As a beginning assembly language programmer, simply use one code segment, one data segment, and one stack segment and leave the segment registers pointing at each of these segments while your program is executing. The assume directive is quite complex and can get you into a considerable amount of trouble if you misuse it. Better not to bother with fancy uses of assume until you are quite comfortable with the whole idea of assembly language programming and segmentation on the 80x86.

The nothing reserved word tells the assembler that you haven't the slightest idea where a segment register is pointing. It also tells the assembler that you're not going to access any data relative to that segment register unless you explicitly provide a segment prefix to an address. A common programming convention is to place assume directives before all procedures in a program. Since segment pointers to declared segments in a program rarely change except at procedure entry and exit, this is the ideal place to put assume directives:


assume ds:P1Dseg, cs:cseg, es:nothing
Procedure1 proc near
push ds ;Preserve DS
push ax ;Preserve AX
mov ax, P1Dseg ;Get pointer to P1Dseg into the
mov ds, ax ; ds register.
.
.
.
pop ax ;Restore ax's value.
pop ds ;Restore ds' value.
ret
Procedure1 endp


The only problem with this code is that MASM still assumes that ds points at P1Dseg when it encounters code after Procedure1. The best solution is to put a second assume directive after the endp directive to tell MASM it doesn't know anything about the value in the ds register:


.
.
.
ret
Procedure1 endp
assume ds:nothing


Although the next statement in the program will probably be yet another assume directive giving the assembler some new assumptions about ds (at the beginning of the procedure that follows the one above), it's still a good idea to adopt this convention. If you fail to put an assume directive before the next procedure in your source file, the assume ds:nothing statement above will keep the assembler from assuming you can access variables in P1Dseg.

Segment override prefixes always override any assumptions made by the assembler. mov ax, cs:var1 always loads the ax register with the word at offset var1 within the current code segment, regardless of where you've defined var1. The main purpose behind the segment override prefixes is handling indirect references. If you have an instruction of the form mov ax,[bx] the assembler assumes that bx points into the data segment. If you really need to access data in a different segment you can use a segment override, thusly, mov ax, es:[bx].

In general, if you are going to use multiple data segments within your program, you should use full segment:offset names for your variables. E.g., mov ax, DSEG1:I and mov bx,DSEG2:J. This does not eliminate the need to load the segment registers or make proper use of the assume directive, but it will make your program easier to read and help MASM locate possible errors in your program.

The assume directive is actually quite useful for other things besides just setting the default segment. You'll see some more uses for this directive a little later in this chapter.

http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_8/CH08-3.html#HEADING3-98

2008年10月16日 星期四

CALL BACK

http://www.dev.idv.tw/mediawiki/index.php/%E4%BD%95%E8%AC%82callback_function%EF%BC%9F

Description
簡單的說,如果你使用了某個function,那麼你就是『call'了一個function。如果系統或是函式是要求你給一個function pointer,這個function pointer指到一個實際的函式(多半這個函式是你自己寫的)。然後它會在適當的時間呼叫此function,則此function就是所謂的 callback function。因為這個function是被『callback'了。

舉一個C的例子來說:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define DEFAULT_BLOCK_SIZE (4096)

// 定義callback function的prototype。
typedef void (* CALLBACK) (int);

// 定義了一個名為ShowPercentage的函式。這就是我們的callback函式。
// 他的prototype必須與前面的CALLBACK宣告一致。
void ShowPercentage(int percentage)
{
fprintf(stderr, "%dn%nn", percentage);
}

// 定義了一個CopyFile的函式,這個函式會將參數source所指定檔案複製到
// target參數所指定的檔案去。而且每複製DEFAULT_BLOCK_SIZE數量的資料
// 就會呼叫一次callback參數所指到function一次。
void CopyFile(const char *source, const char *target, CALLBACK callback)
{
char buf[DEFAULT_BLOCK_SIZE] ;
struct stat fs ;
int fdSrc, fdTrg ;
int readBytes = 0, totalReadBytes = 0, percentage = 0;
fdSrc = open(source, O_RDONLY);
fstat(fdSrc, &fs);
fdTrg = open(target,O_CREAT|O_TRUNC|O_RDWR);
// 主要複製資料的迴圈
while((readBytes=read(fdSrc, buf, DEFAULT_BLOCK_SIZE)) > 0)
{
write(fdTrg, buf, readBytes);
totalReadBytes += readBytes ;
//複製資料後就呼叫callback函式去做顯示百分比的動作。
callback( (totalReadBytes*100)/fs.st_size);
}
close(fdTrg);
close(fdSrc);
}

int main(void)
{
// 這個範例中只是利用callback來顯示目前的進度。
// 實際上我們可以利用callback來做更多的動作。
CopyFile("A.TXT", "B.TXT", ShowPercentage);
return 0 ;
}


2008年10月14日 星期二

簡單的 nmake 使用的 Makefile 範例

# 這是一個簡單的 nmake 使用的 Makefile 範例
# 編譯: c:> nmake
# 清除: c:> nmake clean
# 井民全
#

# 對應 GNU make (如果你想使用 MSYS 編譯的話)
# SRC=$(shell ls *.cpp)
SRC=*.cpp

#替換原則: Objs 檔案群= SRC 檔案群換成 .obj
Objs=$(SRC:cpp=obj)
CFLAGS=-nologo /I"$(VC_DIR)include"

# 主要編譯指令: 先編譯所有的 Objs, 然後建立 exe
all:$(Objs) demo.exe

$(Objs):

# 連結指令
# 注意: 不能用 LINK.exe /OUT:$*.exe $<, 因為建立 exe 檔需要所有的 object 檔案, 不能只有改變的檔
demo.exe: $(Objs)
@echo "Linking ..."
LINK.exe /OUT:$*.exe $(Objs)

# 編譯指令
# 定義所有的 obj 檔 都 depend on .cpp 檔
# 所有 .cpp 更改, 都會重新編譯更改的檔案
.cpp.obj::
echo "Compiling ..."
$(CC) $(CFLAGS) -Fd$O\ -c $<

clean:
del *.obj *.exe

#FAQ
# the list of compiler options
# ms-help://MS.MSDNQTR.2006JAN.1033/vccore/html/vcrefcompileroptionslistedalphabetically.htm

2008年9月8日 星期一

C 字串

http://oaunix.hlhs.hlc.edu.tw/~program/hkin/string.htm#a8

簡 介 下一節 到頁頂


C 沒 有 字 串 型 態 , 因 此 我 們 要 用 字 元 陣 列 來 處 理 字 串 , 例 如 :


char string[] = { 'H', 'e', 'l', 'l', 'o' };

i 0 1 2 3 4
word[i] 'H' 'e' 'l' 'l' 'o'

如 果 要 顯 示 這 個 字 串 , 就 要 用 printf 和 for 迴 圈 逐 個 字 元 顯 示 。

例 子 :

void print_string(char string[], int length) {
int i;

for (i = 0 ; i < i =" 0;" st1 =" %s\nst2" st1 =" Hello" st2 =" World">

main() {
printf("Press Enter to quit\n");
while (getchar() != '\n') ;
}


說 明 :


每 次 呼 叫 getchar 函 數 都 會 等 候 按 鍵 , 按 鍵 後 傳 回 該 鍵 。


例 子 :

#include <stdio.h>

void read_line(char string[], int max_length) {
char ch;
int i = 0;

do {
ch = getchar();
string[i] = ch;
i++;
}
while (ch != '\n' && i <= max_length); string[i-1] = '\0'; }

main() { char string[21]; read_line(string, 20); printf("%s", string); }

執 行 結 果 : This is a line. This is a line.

說 明 : 輸 入 字 串 。
void read_line(char string[], int max_length) 用 read_line 函 數 來 輸 入 字 串 , 可 以 控 制 每 個 輸 入 的 字 元 , 比 scanf 更 有 彈 性 , max_length 是 字 元 數 目 上 限 , 用 來 避 免 字 串 溢 滿 。 while (ch != '\n' && i <= max_length) 如 果 按 Enter 鍵 或 i 大 過 max_length 就 離 開 。 string[i-1] = '\0'; 在 字 串 結 尾 加 入 '\0' 。 其 它 字 串 語 法 上一節 下一節 到頁頂 有 時 一 個 字 串 太 長 , 你 想 分 幾 行 來 寫 它 , 可 以 在 一 行 的 結 尾 寫 反 斜 號 , 例 如 : char lower[] = "abcdefghijklmnopqrstuvwxyz"; 可 以 寫 成 : char lower[] = "abcdefghij\ klmnopqrst\ uvwxyz"; 留 意 字 串 會 由 下 一 行 的 開 頭 繼 續 的 , 所 以 如 果 寫 : char lower[] = "abcdefghij\ klmnopqrst\ uvwxyz"; 就 會 變 成 : char lower[] = "abcdefghij klmnopqrst uvwxyz"; 還 有 另 一 種 更 方 便 的 寫 法 , 就 是 把 一 個 字 串 寫 成 多 個 獨 立 的 字 串 , 例 如 : "OneTwoThree" 可 以 寫 成 : "One" "Two" "Three" 所 以 lower 陣 列 也 可 以 寫 成 : char lower[] = "abcdefghij" "klmnopqrst" "uvwxyz";  

實 例 上一節 下一節 到頁頂 實 例 : 字 數 統 計 上一節 下一節 到頁頂 一 般 文 字 處 理 程 式 都 有 字 數 統 計 功 能 。 假 設 每 個 英 文 字 都 是 由 一 列 連 逐 的 英 文 字 母 組 成 , 例 如 : This is a line. 可 分 為 「 This 」 「 is 」 「 a 」 「 line 」 4 個 字 , 而 : This's a line. 其 中 「 This's 」 可 分 為 「 This 」 「 s 」 兩 個 字 , 因 為 單 引 號 不 是 英 文 字 。

例 子 :

int is_alpha(char ch) { return ('a' <= ch && ch <= 'z') ('A' <= ch && ch <= 'Z'); }
int count_word(char string[]) { int i, count, looking_for_word; count = 0; looking_for_word = 1;

for (i = 0 ; string[i] != '\0' ; i++) { if ( is_alpha(string[i]) ) { if (looking_for_word) { looking_for_word = 0; count++; } } else { looking_for_word = 1; } } return count; } main() { char st[] = "Wow! Great job."; printf( "%s\nWords: %i", st, count_word(st) ); }

執 行 結 果 : Wow! Great job. Words: 3

說 明 : 字 元 可 分 為 兩 類 : 「 英 文 字 母 」 (Alphabet) 和 非 英 文 字 母 , 統 計 方 法 就 是 由 左 至 右 逐 個 字 元 處 理 , 如 果 字 元 不 是 英 文 字 母 , 就 等 待 英 文 字 母 出 現 , 一 出 現 就 把 字 數 加 一 , 然 後 就 不 用 再 等 待 , 直 至 字 元 不 是 英 文 字 母 為 止 。 i   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 string[i]   'W' 'o' 'w' '!' ' ' 'G' 'r' 'e' 'a' 't' ' ' 'j' 'o' 'b' '.' '\0' is_alpha(string[i])   1 1 1 0 0 1 1 1 1 1 0 1 1 1 0 0 looking_for_word 1 0 0 0 1 1 0 0 0 0 0 1 0 0 0 1 1 count 0 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3   int is_alpha(char ch) is_alpha 函 數 檢 查 一 個 字 元 是 否 英 文 字 母 。

looking_for_word 如 果 looking_for_word 是 1 , 表 示 正 等 待 著 下 一 個 字 , 即 是 等 待 英 文 字 母 。 if ( is_alpha(string[i]) ) { if (looking_for_word) { looking_for_word = 0; count++; } 如 果 現 正 等 待 著 下 一 個 字 , 而 string[i] 又 是 英 文 字 母 的 話 , 就 把 字 數 加 一 , 與 及 不 用 等 待 下 一 個 字 。 looking_for_word = 1; 如 果 string[i] 不 是 英 文 字 母 , 就 等 待 下 一 個 字 的 來 臨 。  

實 例 : 基 本 字 串 處 理 上一節 下一節 到頁頂 實 例 : 基 本 字 串 處 理 : 字 串 長 度 上一節 下一節 到頁頂

例 子 :
int string_length(char string[]) { int i; i = 0; while (string[i] != '\0') i++; return i; } main() { char st[21] = "012345"; printf("st = %s ; Length = %i", st, string_length(st)); } 執 行 結 果 : st = 012345 ; Length = 6

說 明 : while (string[i] != '\0') i++; 當 字 元 不 是 '\0' , 就 把 該 字 元 計 算 入 長 度 。

實 例 : 基 本 字 串 處 理 : 字 串 複 製 上一節 下一節 到頁頂 複 製 基 本 型 態 的 資 料 可 以 用 等 於 符 號 , 不 過 它 不 適 用 於 陣 列 , 即 是 你 不 能 寫 : string1 = string2; 所 以 複 製 字 串 必 須 逐 個 字 元 複 製 。 例 子 : void string_copy(char to[], char from[]) { int i; i = 0; while ( (to[i] = from[i]) != '\0' ) i++; } main() { char st1[21] = "012345", st2[21] = "abcde"; printf("Before: st2 = %s\n", st2); string_copy(st2, st1); printf("After : st2 = %s\n", st2); } 執 行 結 果 : Before: st2 = abcde After : st2 = 012345 說 明 : while ( (to[i] = from[i]) != '\0' ) i++; 與 「 字 串 長 度 」 例 子 差 不 多 , 也 是 檢 查 字 元 是 否 '\0' 。 這 句 會 先 執 行 : to[i] = from[i] 然 後 才 把 from[i] 的 字 元 與 '\0' 比 較 , 即 是 先 複 製 , 後 檢 查 , 因 此 在 迴 圈 完 結 時 , to 字 串 結 尾 會 有 '\0' 。

實 例 : 基 本 字 串 處 理 : 字 串 比 較 上一節 下一節 到頁頂 你 也 不 能 寫 : if (string1 == string2) 來 比 較 字 串 , 而 正 確 的 做 法 就 是 把 兩 個 字 串 內 的 所 有 對 應 的 字 元 作 比 較 , 如 果 有 一 個 字 元 不 同 的 話 , 就 算 是 不 相 等 了 , 例 如 : char string1[] = "abcde"; char string2[] = "abdde"; 因 為 string1[2] 是 'c' , string2[2] 是 'd' , 所 以 它 們 不 相 等 。 其 實 , 字 串 也 可 以 好 像 數 目 一 樣 , 把 兩 樣 東 西 作 比 較 , 會 有 3 種 情 況 : 等 於 、 大 於 及 小 於 。

什 麼 ? 文 字 也 有 大 小 之 分 嗎 ? 對 啊 , 還 記 得 ASCII 碼 嗎 ? 每 個 字 元 都 有 一 個 碼 , 我 們 可 以 跟 據 這 個 碼 來 決 定 字 元 的 大 小 , 而 比 較 方 法 很 簡 單 , 只 需 把 兩 個 字 串 由 頭 至 尾 逐 個 字 元 作 比 較 便 可 , 所 謂 的 「 由 頭 至 尾 」 , 就 是 由 指 數 0 的 字 元 開 始 , 直 至 出 現 不 同 的 字 元 , 或 者 到 了 字 串 結 尾 。 為 什 麼 不 是 「 由 尾 至 頭 」 呢 ? 可 能 沒 有 實 際 用 途 吧 , 因 為 「 由 頭 至 尾 」 可 以 把 英 文 字 排 列 到 好 像 字 典 一 樣 。 例 如 比 較 以 上 的 string1 和 string2 , 首 先 比 較 指 數 0 字 元 , 兩 者 都 是 'a' , 然 後 比 較 指 數 1 字 元 , 兩 者 都 是 'b' , 然 後 比 較 指 數 2 字 元 , string1[2] 是 'c' , 它 的 ASCII 碼 是 63h , 而 string2[2] 是 'd' , 它 的 ASCII 碼 是 64h , 所 以 'd' 比 'c' 大 , 即 是 string2 比 string1 大 。 我 們 可 以 寫 個 函 數 , 分 別 傳 回 0 、 1 和 -1 來 代 表 等 於 、 大 於 及 小 於 , 例 如 : int string_compare(char st1[], char st2[]); 情況 傳回 st1 等 於 st2 0 st1 大 於 st2 1 st1 小 於 st2 -1  

例 子 : int string_compare(char st1[], char st2[]) { int i; i = 0; while (st1[i] == st2[i] && st1[i] != '\0') i++; if (st1[i] == st2[i]) { return 0; } else { return (st1[i] > st2[i])? 1 : -1;
}
}

main() {
char st1[21] = "abcde", st2[21] = "abdde", st3[21] = "abcdefg";

printf("%s compare %s = %i\n", st1, st2, string_compare(st1, st2));
printf("%s compare %s = %i\n", st1, st1, string_compare(st1, st1));
printf("%s compare %s = %i\n", st2, st1, string_compare(st2, st1));
printf("%s compare %s = %i\n", st1, st3, string_compare(st1, st3));
}

執 行 結 果 :

abcde compare abdde = -1
abcde compare abcde = 0
abdde compare abcde = 1
abcde compare abcdefg = -1


說 明 :



i = 0;
while (st1[i] == st2[i] && st1[i] != '\0') i++;

由 指 數 0 開 始 比 較 , 直 至 出 現 不 相 同 字 元 或 st1[i] 是 結 尾 字 元 , 所 以 執 行 迴 圈 後 , 指 數 i 的 字 元 是 不 相 同 的 字 元 , 或 者 是 '\0' 。 為 什 麼 只 檢 查 st1[i] 而 不 用 檢 查 st2[i] 呢 ? 即 為 什 麼 不 寫 :


while (st1[i] == st2[i] && st1[i] != '\0' && st2[i] != '\0') i++;

因 為 如 果 st1[i] 等 於 st2[i] , 那 麼 st1[i] 不 等 於 什 麼 , 也 代 表 st[2] 不 等 於 什 麼 , 所 以 檢 查 st1 或 st2 也 沒 所 謂 , 不 必 兩 個 都 檢 查 了 。


if (st1[i] == st2[i]) {
return 0;
} else {
return (st1[i] > st2[i])? 1 : -1;
}

如 果 st1[i] 等 於 st2[i] , 就 表 示 兩 個 字 串 是 相 等 的 , 因 此 傳 回 0 , 這 時 候 , st1[i] 和 st2[i] 都 會 等 於 '\0' 。 如 果 它 們 不 相 等 , 就 跟 據 它 們 的 大 小 來 傳 回 1 或 -1 。


abcde compare abcdefg = -1

兩 個 不 同 長 度 的 字 串 也 沒 有 特 別 , 當 比 較 到 "abcdefg" 的 'f' 時 , 剛 好 到 了 另 一 字 串 的 '\0' :

a b c d e \0    
a b c d e f g \0

'f' 的 ASCII 碼 是 66h , '\0' 的 是 0 , 因 此 "abcde" 比 "abcdefg" 小 。


string_compare 函 數 好 像 Perl 的 cmp 運 算 子 。


實 例 : 基 本 字 串 處 理 : 字 串 加 法 上一節 下一節 到頁頂


兩 個 字 串 相 加 . 即 是 把 其 中 一 個 字 串 接 駁 到 另 一 字 串 的 尾 部 。

例 子 :

void string_concat(char st1[], char st2[], char result[]) {
int i, j;

i = 0;
while ( (result[i] = st1[i]) != '\0' ) i++;
j = i;
while ( (result[i] = st2[i - j]) != '\0' ) i++;
}

main() {
char st1[21] = "abcde", st2[21] = "012345", st3[21];

string_concat(st1, st2, st3);
printf("%s + %s = %s\n", st1, st2, st3);
}

執 行 結 果 :

abcde + 012345 = abcde012345


說 明 :



i = 0;
while ( (result[i] = st1[i]) != '\0' ) i++;

先 把 st1 複 製 到 result 。


j = i;
while ( (result[i] = st2[i - j]) != '\0' ) i++;

然 後 複 製 st2 。



實 例 : 基 本 字 串 處 理 : 字 串 位 置 上一節 下一節 到頁頂


尋 找 某 個 子 字 串 在 另 一 字 串 的 開 始 位 置 。

例 子 :

/* Insert "string_length" function here */

int index(char st[], char subst[]) {
int st_start, sti, substi, limit, result, st_length, subst_length;

result = -1;
st_length = string_length(st);
subst_length = string_length(subst);

limit = st_length - subst_length;
if (limit < st_start =" 0" sti =" st_start;" substi =" 0;" substi ="="" result =" st_start;" s =" %i\n" s =" %i\n" s =" %i\n" 012345 =" 0" 012345 =" 3" 012345 =" -1" result =" -1;" limit =" st_length" st_start =" 0" 3 =" limit" substi ="="" i =" 0;" st1 =" %s\n" 3 =" %s\n" 100 =" %s\n" 1 =" %s\n" 0 =" %s\n" st1 =" 012345" 3 =" 012" 100 =" 5" 1 =" Index" 0 =" 說" st_length =" string_length(st);" insert_length =" string_length(insert);" i =" st_length">= start ; i--) {
st[i + insert_length] = st[i];
}

for (i = 0 ; i < before =" %s\n" after =" %s\n" before =" 012345" after =" 01bcd2345" i =" st_length">= start ; i--) {
st[i + insert_length] = st[i];
}

搬 遷 位 置 start 或 之 後 的 字 元 , 包 括 '\0' , 以 便 把 insert 放 到 空 出 來 的 位 置 。

搬遷前 0 1 2 3 4 5 \0      
搬 遷 後 0 1 2 3 4 2 3 4 5 \0
插 入 後 0 1 b c d 2 3 4 5 \0

 


for (i = 0 ; i < insert_length ; i++) {
st[start + i] = insert[i];
}

插 入 insert 。


 


實 例 : 基 本 字 串 處 理 : 字 串 移 除 上一節 下一節 到頁頂


例 子 :

/* Insert "string_length" function here */

void string_remove(char st[], int start, int length) {
int i, limit;

if ( string_length(st) <= start ) return;

i = start + 1;
limit = start + length;
while (st[i] != '\0' && i < limit) i++;

while ( (st[i - length] = st[i]) != '\0' ) i++;
}

main() {
char st1[21] = "012345";

printf("st1 = %s\n", st1);
string_remove(st1, 2, 3);
printf("Index 2, length 3 = %s\n", st1);
}

執 行 結 果 :

st1 = 012345
Index 2, length 3 = 015


說 明 :



i = start + 1;
limit = start + length;
while (st[i] != '\0' && i < limit) i++;

去 到 要 移 除 的 字 串 的 後 一 個 字 元 , 即 i = limit = 5 , 或 去 到 '\0' 。 然 後 把 字 串 剩 餘 的 部 份 , 包 括 '\0' , 複 製 到 start 位 置 。

複製前 0 1 2 3 4 5 \0
複 製 後 0 1 5 \0 4 5 \0

 


while ( (st[i - length] = st[i]) != '\0' ) i++;

把 字 串 剩 餘 的 部 份 複 製 到 start 位 置 。


 


實 例 : 基 本 字 串 處 理 : 字 串 取 代 上一節 下一節 到頁頂


例 子 :

/* Insert "string_length" function here */
/* Insert "string_remove" function here */
/* Insert "string_insert" function here */

void string_replace(char st[], char out[], char in[]) {
int i, out_length;

i = index(st, out);
if (i != -1) {
out_length = string_length(out);
string_remove(st, i, out_length);
string_insert(st, in, i);
}
}

main() {
char st1[21] = "012345";

printf("Before, st1 = %s\n", st1);
string_replace(st1, "123", "OneTwoThree");
printf("After, st1 = %s\n", st1);
}

執 行 結 果 :

Before, st1 = 012345
After, st1 = 0OneTwoThree45


說 明 :



/* Insert "string_length" function here */
/* Insert "string_remove" function here */
/* Insert "string_insert" function here */

string_replace 函 數 會 用 到 string_length 、 string_remove 和 string_insert 函 數 , 其 運 作 原 理 是 : 找 尋 要 移 除 的 字 串 , 如 果 找 到 , 就 移 除 它 , 然 後 插 入 新 的 字 串 。


i = index(st, out);

找 尋 要 移 除 的 字 串 , 傳 回 來 的 子 字 串 位 置 i 會 用 在 string_remove 和 string_insert 函 數 。


if (i != -1)

如 果 找 到 的 話 。



實 例 : 字 典 上一節 下一節 到頁頂


本 節 介 紹 一 個 字 典 程 式 , 它 讓 你 輸 入 英 文 字 , 然 後 會 顯 示 它 的 解 釋 。 你 可 以 定 義 一 個 「 字 典 條 目 」 (Entry) 結 構 :


struct entry {
char word[11];
char definition[31];
}

一 個 條 目 包 括 英 文 字 word 和 解 釋 definition 。 假 設 這 個 字 典 有 5 個 條 目 , 你 可 以 用 結 構 陣 列 來 儲 存 這 個 字 典 :


struct entry dictionary[5];

這 樣 就 可 以 寫 :


dictionary[i].word
dictionary[i].definition

來 表 示 第 i 個 條 目 的 英 文 字 和 解 釋 。

你 可 以 寫 個 函 數 來 搜 尋 這 個 字 典 :


entry_index = lookup(dictionary, word, num_entry);

word 是 要 查 詢 的 英 文 字 , num_entry 是 條 目 數 目 , entry_index 是 word 在 dictionary 陣 列 的 位 置 。

例 子 :

struct entry {
char word[11];
char definition[51];
};

/* Insert "string_compare" function here */

int lookup(struct entry dictionary[], char word[], int num_entry) {
int i;

for (i = 0 ; i < num_entry ; i++) {
if ( string_compare(word, dictionary[i].word) == 0 ) return i;
}

return -1;
}

main() {
struct entry dictionary[5] = {
{"apple", "round fruit with firm juicy flesh"},
{"boy", "male child"},
{"cat", "small furry domesticated animal"},
{"dog", "common domestic animal kept by human"},
{"egg", "the cell from which the young is formed"},
};
char word[11];
int num_entry = 5, entry_index;

printf("Type '9' to quit.\n");
while (1) {
printf("Enter word: ");
scanf("%10s", word);

if (word[0] == '9') break;

entry_index = lookup(dictionary, word, num_entry);

if (entry_index != -1) {
printf("%s\n\n", dictionary[entry_index].definition);
} else {
printf("Sorry, the word is not in my dictionary.\n\n");
}
}
}

執 行 結 果 :

Type '9' to quit.
Enter word: apple
round fruit with firm juicy flesh

Enter word: egg
the cell from which the young is formed

Enter word: fork
Sorry, the word is not in my dictionary.

Enter word: 9


說 明 :


lookup 函 數 會 傳 回 查 詢 字 串 在 字 典 中 的 指 數 , 如 果 找 不 到 字 串 , 就 傳 回 -1 。



總 結 上一節 到頁頂


C 的 字 串 結 尾 字 元 是 '\0' , 使 用 結 尾 字 元 的 好 處 是 不 用 留 意 字 串 長 度 。