2010年5月25日 星期二

SETLOCAL指令

在bat檔中,常常會遇到在IF或是FOR裡面做完運算之後,
直接在下一行確沒有辦法讀到上一行的結果~

例如:
@echo off
set Val=1
echo %Val%
(
set Val=2
echo %Val%
set Val=3
echo %Val%
)
echo %Val%
pause


我哩!我明明有在裡面變更Val的值,都是為啥印出來確都沒有變哩~
而是只有執行到括號的結束之後,整個值才有變。
那這樣有啥用啊~

今天在網路上,不小心看到一個新指令SETLOCAL

說明如下:
SETLOCAL 批次命令現在可接受一個選項引數,這個引數可為:
ENABLEEXTENSIONS / DISABLEEXTENSIONS
啟用或停用命令處理擴充功能。
請用 CMD /? 來查看說明。
ENABLEDELAYEDEXPANSION / DISABLEDELAYEDEXPANSION
啟用或停用延遲環境變數擴充功能。
請用 SET /? 來查看說明。
這些修正命令作用會持續到對應 ENDLOCAL 命令為止,而不管
它們的設定是否在 SETLOCAL 命令之前。


範例程式如下:
@echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set Val=1
echo %Val% !Val!
(
set Val=2
echo %Val% !Val!
set Val=3
echo %Val% !Val!
)
echo %Val% !Val!
pause

輸出結果如下:
1 1
1 2
1 3
3 3
請按任意鍵繼續 . . .


喔耶,這樣就可以在IF或是FOR做運算的動作,同時,也可以在括號裡面做判斷啦~

批次檔 有沒有類似 IF...THEN....ELSE 跟 OR的語法呢??
以上聯結的重點
在 %xxx% 的使用是在 執行 指令時...將 %xxx% 的 值 取代(展開) %xxx% 到指令句中
而在 if for 和 ( ) 指令 執行 時...這扯到指令處理的方式
在我寫的範疇中... 在處理 ( ) 時... 是將 ( ) 其中的指令一起處理
因此 ( ) 中在實際執行時...就把 %Val% 取代了... 而執行 ( ) 之前 Val 是 =1
set Val=1
(
set Val=2
echo 1 !Val!
set Val=3
echo 1 !Val!
)

即 ( ) 是將 %Val% 一次 展開
所以 !xxx! 的意思和 %xxx% 不一樣
!xxx! 是使用時 (該指令執行時) 才取代(展開)
這就是 "延遲"環境變數展開功能

簡單說就是 %xxxx% !xxxx! 展開 的 時間點 不同

( ) 是 CMD 本身的 指令區段 的寫法
而 if for 使用時也是有 指令區段 ...只不過如果只有一行...可以省略不打 ( ) 而以
不過也是要看情況...例如 if
if "a"=="b" echo 1
等於
if "a"=="b" (echo 1)

if "a"=="b" echo 1 else echo 2
等於
if "a"=="b" (echo 1 else echo 2)
所以正常要用 else 要打成
if "a"=="b" (echo 1) else (echo 2)

而基本上...每行指令...本身就有一個指令區段

set a=1
set a=2&echo %a%



結果是顯示 1
因為實際上等於是
(set a=1)
(set a=2&echo %a%)
而這等同
(set a=1)
(
set a=2
echo %a%
)

也就是 ( ) 視為一個指令(指令區段)
而 %xxxx% 展開 是在 執行 指令(指令區段) 時就先展開
而 !xxxx! 是在執行 指令區段 中的 單行指令 時才展開

參考資料:
批次檔 有沒有類似 IF...THEN....ELSE 跟 OR的語法呢??

2010年5月13日 星期四

抽小版的Script

這一個bat script必需要搭配Beyond Compare才能使用

進化版本
sg.bat
@ECHO OFF

REM ========== ----------------------- ==========
REM ========== Solution Generator (sg) ==========
REM ========== ----------------------- ==========

REM ========== INPUT NEW CODE PATH (DO NOT MODIFY) ==========

:TryGetNewPath
SET NEW_CODE_PATH=
SET /P NEW_CODE_PATH=Please input the new(modified) code path (default: %SOURCE_ROOT%\%PROJECT_NAME%):
IF "%NEW_CODE_PATH%" EQU "" SET NEW_CODE_PATH=%SOURCE_ROOT%\%PROJECT_NAME%
IF EXIST %NEW_CODE_PATH% GOTO NewPathOk
ECHO THE PATH DO NOT EXIST, PLEASE INPUT AGAIN
GOTO TryGetNewPath
:NewPathOk

REM ========== INPUT OLD CODE PATH (DO NOT MODIFY) ==========
:TryGetOldPath
SET /P OLD_CODE_PATH=Please input the old(original) code path (ex: d:\work\old\ or d:\work\old.zip):
IF EXIST %OLD_CODE_PATH% GOTO OldPathOk
ECHO THE PATH DO NOT EXIST, PLEASE INPUT AGAIN
GOTO TryGetOldPath
:OldPathOk


REM ======== GET DATE AND TIME (DO NOT MODIFY) ========
FOR /F "tokens=1-4 delims=/ " %%a IN ("%date%") DO (
SET SYS_DATE=%%a.%%b.%%c
)
FOR /F "tokens=1-4 delims=:." %%e IN ("%time%") DO (
SET SYS_TIME=%%e%%f%%g
)

REM ========== INPUT MINI-SOLUTION PATH (DO NOT MODIFY) ==========
SET SOLUTION_PATH=
SET /P SOLUTION_PATH=Please input mini-solution path (default: %SOURCE_ROOT%\%SYS_DATE%-%SYS_TIME%-mini-solution):
IF "%SOLUTION_PATH%" EQU "" SET SOLUTION_PATH=%SOURCE_ROOT%\%SYS_DATE%-%SYS_TIME%-mini-solution

REM ===========================================================
REM User must modify the setting of this block.
REM GENERATE_MODE : SEPARATE_MODE or MERGE_MODE
REM NEW_CODE_PATH : new revision code path
REM OLD_CODE_PATH : old revision code path
REM TARGET_PATH : little version code path
REM SCRIPT_PATH : script file path (solution_generator.script and solution_generator.bat)

SET GENERATE_MODE=SEPARATE_MODE
SET NEW_CODE_PATH=%NEW_CODE_PATH%
SET OLD_CODE_PATH=%OLD_CODE_PATH%
SET TARGET_PATH=%SOLUTION_PATH%
SET SCRIPT_PATH=D:\DEVTLS\BAT

REM ===========================================================
REM Internel path setting , use default path.
REM User can modify these if need.

SET BC2_PATH="C:\Program Files\Beyond Compare 2"
SET TEMP_PATH=C:\TEMP_CODE
IF /I "%GENERATE_MODE%" == "SEPARATE_MODE" SET GENERATE_NEW_CODE=%TARGET_PATH%\NEW
IF /I "%GENERATE_MODE%" == "SEPARATE_MODE" SET GENERATE_OLD_CODE=%TARGET_PATH%\OLD

REM ===========================================================
MKDIR %TARGET_PATH%
IF /I "%GENERATE_MODE%" == "MERGE_MODE" MKDIR %TEMP_PATH%
IF /I "%GENERATE_MODE%" == "SEPARATE_MODE" MKDIR %GENERATE_NEW_CODE%
IF /I "%GENERATE_MODE%" == "SEPARATE_MODE" MKDIR %GENERATE_OLD_CODE%
SET ORIGIN_PATH=%PATH%
SET PATH=%PATH%;%BC2_PATH%
IF /I "%GENERATE_MODE%" == "MERGE_MODE" bc2.exe @%SCRIPT_PATH%\solution_generator.script %NEW_CODE_PATH% %OLD_CODE_PATH% %TARGET_PATH% %TEMP_PATH%
IF /I "%GENERATE_MODE%" == "SEPARATE_MODE" bc2.exe @%SCRIPT_PATH%\solution_generator.script %NEW_CODE_PATH% %OLD_CODE_PATH% %GENERATE_NEW_CODE% %GENERATE_OLD_CODE%
IF /I "%GENERATE_MODE%" == "MERGE_MODE" FOR /R %TEMP_PATH% %%I IN (*.*) DO MOVE /Y %%~fnxI %%~fnxI_
IF /I "%GENERATE_MODE%" == "MERGE_MODE" XCOPY %TEMP_PATH% %TARGET_PATH% /E /K /Y
IF /I "%GENERATE_MODE%" == "MERGE_MODE" RMDIR /S /Q %TEMP_PATH%
SET %PATH%=%ORIGIN_PATH%

REM ========== CREATE NOTE FILE (DO NOT MODIFY) ==========
SET %NoteFile%=%SOLUTION_PATH%\History.txt
ECHO NEW_CODE_PATH: %NEW_CODE_PATH% > %NoteFile%
ECHO OLD_CODE_PATH: %OLD_CODE_PATH% >> %NoteFile%
ECHO MINI-SOLUTION PATH: %SOLUTION_PATH% >> %NoteFile%
ECHO ============================= >> %NoteFile%
ECHO #; Tag: >> %NoteFile%
ECHO #; Issue: >> %NoteFile%
ECHO #; Category: >> %NoteFile%
ECHO #; Symptom: >> %NoteFile%
ECHO #; RootCause: >> %NoteFile%
ECHO #; Solution: >> %NoteFile%
ECHO #; Description: >> %NoteFile%
ECHO #; >> %NoteFile%
ECHO #; Related Files: >> %NoteFile%
ECHO #; Added: > %NoteFile%
ECHO #; Deleted: >> %NoteFile%
ECHO #; Modified: >> %NoteFile%
ECHO #; >> %NoteFile%

PAUSE
REM ===========================================================

solution_generator.script
criteria rules-based
filter "-.svn\";"-uefi64\";"-uefi32\";"-X64\";"-*.Dep";"-_svn\";"-uefi64ddt\"
load "%1" "%2"
expand all
select lt.diff.files lt.orphan.files
copyto lt path:base "%3"
select rt.diff.files rt.orphan.files
copyto rt path:base "%4"


顯示目前目錄底下的所有檔案
% dir /s /b


參考資料:
Eric's Webspace Windows 95 / DOS 7 Batch Programming

透過Bat計算編譯的時間

以下的程式碼,可以協助使用者編譯程式,像是原本要打nmake uefi64
只要直接打
% vb
就可以啦,vb是variable build的簡稱啦~
若要執行nmake clean,可以打
% vb clean

這裡要注意的是,在bat中,%是轉換為其它進位,真正餘數運算元是%%

以下是程式碼
vb.bat
@ECHO OFF

REM ========== GET THE START TIME (DO NOT MODIFY) ==========
SET StartTime=%time%
FOR /F "delims=:. tokens=1-3" %%p IN ("%StartTime%") DO (
SET /A StartTotalTime=%%p*60*60+%%q*60+%%r
)

REM ========== SET THE DEFAULT VALUE (DO NOT MODIFY) ==========
IF "%1" EQU "" (
SET TARGET=uefi64
) ELSE (
SET TARGET=%1
)

REM ========== EXECUTE NMAKE (DO NOT MODIFY) ==========
ECHO NMAKE %TARGET%
CALL NMAKE %TARGET%
@ECHO OFF
GOTO END

:END

REM ========== GET THE END TIME (DO NOT MODIFY) ==========
SET EndTime=%time%
FOR /F "delims=:. tokens=1-3" %%p IN ("%EndTime%") DO (
SET /A EndTotalTime=%%p*60*60+%%q*60+%%r
)

REM ========== Summary (DO NOT MODIFY) ==========
SET /A TotalTime=%EndTotalTime%-%StartTotalTime%
SET /A Hour=%TotalTime%/3600
SET /A Second=(%TotalTime%)%%60
SET /A Minute=(%TotalTime%-%Hour%*3600-%Second%)/60
ECHO Total: %Hour%:%Minute%:%Second% (From %StartTime% to %EndTime%)

REM ========== Jump the Hint Window (DO NOT MODIFY) ==========
cscript D:\Devtls\Bat\CompilerDoneMsg.vbs %TARGET% //Nologo


CompilerDoneMsg.vbs
If WScript.Arguments.Count() = 0 Then
MsgBox " CompilerDone ", vbSystemModal
Else
'
'Display first Argument only.
'
MsgBox " CompilerDone ", vbSystemModal, WScript.Arguments(0)
End If


參考資料:
如何用批處理求餘數

2010年5月3日 星期一

[組語] PROC & ENDP

; Caspar-comment - PROC用來定義程序,以ENDP表示結束
; Caspar-comment - 若sub function與main function在同一段Code則使用NEAR, 主程式的Stack只會存入ip
; Caspar-comment - 若sub function與main function不在同一段Code則使用FAR, 主程式的Stack會存入cs, ip的值

FooStart         PROC NEAR C PUBLIC


CALL 指令

用來呼叫副程式的指令,語法是

CALL 副程式名

RET 指令

RET 指令是在副程式中返回主程式的指令,其語法是:

RET n
RETN n
RETF n

n 是位元組數,此位元組數表示自副程式返回時還要再使 SP 加上幾個位元組。常用於高階語言呼叫副程式,見第 31 章。RETN、RETF 見 PROC/ENDP 假指令的說明。
PROC/ENDP 假指令

PROC 並非 80x86 指令而是假指令,它是用來定義副程式,它指出副程式由此開始,必須和 EDNP 搭配使用,否則組譯器會出錯。語法是:

標號名 proc [near/far]
;副程式碼
ret
標號名 endp

標號名就是這個副程式的名稱,例如當 CPU 執行到

CALL my_subroutine

指令時,就會跳到名為 my_subroutne 的副程式執行。

若 PROC 後接 near 表示副程式與主程式在同一區段內,稱為近程呼叫;若接 far,表示副程式與主程式在不同區段內,稱為遠程呼叫。若省略 near 和 far,組譯器能自動判斷副程式與主程式是否在同一區段內。近程呼叫或遠程呼叫會影響返回位址的大小。很明顯的,近程呼叫,只要把下一指令的偏移位址推入堆疊即可而偏移位址只有一個字組的大小;如果是遠程呼叫,就要把區段位址及偏移位址,共兩個字組推入堆疊。

這也影響到返回時要自堆疊取出一個或兩個字組,因此分別用 RETN、RETF 來表示。但是如果用 RET,也沒關係,組譯器也能自動判斷。


可多參考INTEL和AMD在CPU指令手册

下面有一個例子
;------------------------------------------------------------------------------
; VOID
; IoOutput32 (
; IN UINT16 Port,
; IN UINT32 Data
; )
;------------------------------------------------------------------------------
IoOutput32 PROC PUBLIC Port:UINT16, Data:UINT32
pushad
mov eax, Data
mov dx, Port
out dx, eax
popad
ret
IoOutput32 ENDP

參考資料:
asm - 副程式

[組語] ALIGN

; Caspar-comment - 以下的程式碼以4 byte為單位在排列
align 4

[組語] SEGMENT

之前已經提過 SEGMENT 假指令了,事實上 SEGMENT 後面有許多選項可供選擇,當您寫副程式製作成程式庫,或是寫很大的程式把它切割成數個小程式再合併時,要注意這些選項。其語法是:

區段名 segment 排列型式 合併型式 類別名

排列型式 (align type) 是告訴連結程式該區段由那一種位址開始,或者說此區段排在前一區段後的那一種位址開始。可以選擇的位址種類有下面幾種:

1. byte:由某一位元組開始,也就是任意位址開始,視載入 DOS 驅動程式多寡以及前面區段大小決定。
2. word:由某一字組開始,也就是從偶數位址開始。
3. dword:由某個雙字組開始,一個雙字組是 4 個位元組。
4. para:由某一節開始,節的英文是 paragraph,一節的大小是 10H 個位元組,也就是十進位的 16 個位元組。
5. page:由某一頁 (page) 開始,一頁是 100H 個位元組。

前面幾章的例子中,都省略排列形式,則連結器自動用 para 選項,也就是說沒有排列形式時,MASM 內定選項是 para。

合併型式 (combine type) 是告訴連結器該區段和程式庫內或其他目的檔內的那一個區段連結在一起成為一個區段。可以用的選項有:

1. none:不與其他區段合併。
2. public:具有相同名稱且相同類別名的區段合併成一個區段。
3. common:具有相同名稱的區段合併成一個區段。
4. at 位址:區段被置於特定位址。
5. stack:表示此區段為堆疊區段。
6. memory:使區段置於最高位址。

雖然 segment 的用法很複雜,但是最常用的還是『stack』、『public』。因為在主程式中要使用程式庫,或者要把某個副程式加入程式庫都必須宣告『public』及類別名,使連結程式 (LINK.EXE) 能順利把各區段正確連結。
參考資料:
SEGMENT 假指令

[組語] .module

tiny  用於成立 .com 檔,程式段、資料段、額外段及堆疊段等四個區共用在同一個區段(64Kb)中,檔案最大僅能64Kb。
small 程式段及資料段,各有64Kb的空間。為一般最常用的 .exe 檔的模式,建議使用。
medium 程式段可超過64Kb,而資料段只有64Kb的空間。
compact 資料段可超過64Kb,而程式段只有64Kb的空間。
large 程式段與資料段,皆可大於 64 Kb。
huge 同上,但更強的是,連同資料段中的任意某陣列,也可大於 64Kb 的範圍。
flat ???? 用於 OS/2 系統。用途不詳......


MODEL SMALL, C 就指定要和C 語言聯結
參考資料:
組合語言假指令

[組語] INCLUDE

跟C語言中的#include是一樣的意思~
就是把後面的這一個檔案,整個把它貼到目前的這一個位置
INCLUDE function.inc

以上的程式就是把function.inc整個貼到目前的這一個位置