<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 第30章 16位Windows 16位windows程序現在很少見了,但是在舊式計算機或者入侵軟件狗的時候(58章),我有時候還會遇到這個問題。 16位的windows版本最高到3.11,95(*注:作者筆誤寫成了Win96)/98/ME也支持16位代碼,他們同時也是一個Windows NT家族的32位版本。64位版本的Windows NT家族完全不支持16位程序。 代碼類似于MS-DOS代碼。 執行文件并不是MZ式或者PE文件,而是NE式(所謂的“New Executable”,新執行程序)。 所有的例子都由OpenWatcom 1.9編譯器編譯,使用這些參數: ``` Wcl.exe -i=C:/WATCOM/h/win/ -s -os -bt=windows example.c ``` ## 30.1 例子#1 ``` #include <windows.h> int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { MessageBeep(MB_ICONEXCLAMATION); return 0; }; WinMain proc near push bp mov bp, sp mov ax, 30h ; ’0’ ; MB_ICONEXCLAMATION constant push ax call MESSAGEBEEP xor ax, ax ; return 0 pop bp retn 0Ah WinMain endp ``` 到現在為止,看起來都很簡單。 ## 30.2 例子#2 ``` #include <windows.h> int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { MessageBox (NULL, "hello, world", "caption", MB_YESNOCANCEL); return 0; }; WinMain proc near push bp mov bp, sp xor ax, ax ; NULL push ax push ds mov ax, offset aHelloWorld ; 0x18\. "hello, world" push ax push ds mov ax, offset aCaption ; 0x10\. "caption" push ax mov ax, 3 ; MB_YESNOCANCEL push ax call MESSAGEBOX xor ax, ax ; return 0 pop bp retn 0Ah WinMain endp dseg02:0010 aCaption db ’caption’,0 dseg02:0018 aHelloWorld db ’hello, world’,0 ``` 有兩個重要的信息:PASCAL調用轉換表明先傳遞最后的參數(MB_YESNOCANCEL),然后才是第一個參數NULL。這個調用也表明了調用者恢復棧指針:因為RETN有一個0Ah的參數,這個意味著棧指針將在函數退出時上移10個字節。 指針按對傳遞:一組數據先傳遞,指針就在這組數據里面。例子這里只有一組數據,所以DS永遠指向可執行文件的data段。 ## 30.3 例子#3 ``` #include <windows.h> int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { int result=MessageBox (NULL, "hello, world", "caption", MB_YESNOCANCEL); if (result==IDCANCEL) MessageBox (NULL, "you pressed cancel", "caption", MB_OK); else if (result==IDYES) MessageBox (NULL, "you pressed yes", "caption", MB_OK); else if (result==IDNO) MessageBox (NULL, "you pressed no", "caption", MB_OK); return 0; }; WinMain proc near push bp mov bp, sp xor ax, ax ; NULL push ax push ds mov ax, offset aHelloWorld ; "hello, world" push ax push ds mov ax, offset aCaption ; "caption" push ax mov ax, 3 ; MB_YESNOCANCEL push ax call MESSAGEBOX cmp ax, 2 ; IDCANCEL jnz short loc_2F xor ax, ax push ax push ds mov ax, offset aYouPressedCanc ; "you pressed cancel" jmp short loc_49 ; --------------------------------------------------------------------------- loc_2F: cmp ax, 6 ; IDYES jnz short loc_3D xor ax, ax push ax push ds mov ax, offset aYouPressedYes ; "you pressed yes" jmp short loc_49 ; --------------------------------------------------------------------------- loc_3D: cmp ax, 7 ; IDNO jnz short loc_57 xor ax, ax push ax push ds mov ax, offset aYouPressedNo ; "you pressed no" loc_49: push ax push ds mov ax, offset aCaption ; "caption" push ax xor ax, ax push ax call MESSAGEBOX loc_57: xor ax, ax pop bp retn 0Ah WinMain endp ``` 就是前一節的擴展而已。 ## 30.4 例子#4 ``` #include <windows.h> int PASCAL func1 (int a, int b, int c) { return a*b+c; }; long PASCAL func2 (long a, long b, long c) { return a*b+c; }; long PASCAL func3 (long a, long b, long c, int d) { return a*b+c-d; }; int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { func1 (123, 456, 789); func2 (600000, 700000, 800000); func3 (600000, 700000, 800000, 123); return 0; }; func1 proc near c = word ptr 4 b = word ptr 6 a = word ptr 8 push bp mov bp, sp mov ax, [bp+a] imul [bp+b] add ax, [bp+c] pop bp retn 6 func1 endp func2 proc near arg_0 = word ptr 4 arg_2 = word ptr 6 arg_4 = word ptr 8 arg_6 = word ptr 0Ah arg_8 = word ptr 0Ch arg_A = word ptr 0Eh push bp mov bp, sp mov ax, [bp+arg_8] mov dx, [bp+arg_A] mov bx, [bp+arg_4] mov cx, [bp+arg_6] call sub_B2 ; long 32-bit multiplication add ax, [bp+arg_0] adc dx, [bp+arg_2] pop bp retn 12 func2 endp func3 proc near arg_0 = word ptr 4 arg_2 = word ptr 6 arg_4 = word ptr 8 arg_6 = word ptr 0Ah arg_8 = word ptr 0Ch arg_A = word ptr 0Eh arg_C = word ptr 10h push bp mov bp, sp mov ax, [bp+arg_A] mov dx, [bp+arg_C] mov bx, [bp+arg_6] mov cx, [bp+arg_8] call sub_B2 ; long 32-bit multiplication mov cx, [bp+arg_2] add cx, ax mov bx, [bp+arg_4] adc bx, dx ; BX=high part, CX=low part mov ax, [bp+arg_0] cwd ; AX=low part d, DX=high part d sub cx, ax mov ax, cx sbb bx, dx mov dx, bx pop bp retn 14 func3 endp WinMain proc near push bp mov bp, sp mov ax, 123 push ax mov ax, 456 push ax mov ax, 789 push ax call func1 mov ax, 9 ; high part of 600000 push ax mov ax, 27C0h ; low part of 600000 push ax mov ax, 0Ah ; high part of 700000 push ax mov ax, 0AE60h ; low part of 700000 push ax mov ax, 0Ch ; high part of 800000 push ax mov ax, 3500h ; low part of 800000 push ax call func2 mov ax, 9 ; high part of 600000 push ax mov ax, 27C0h ; low part of 600000 push ax mov ax, 0Ah ; high part of 700000 push ax mov ax, 0AE60h ; low part of 700000 push ax mov ax, 0Ch ; high part of 800000 push ax mov ax, 3500h ; low part of 800000 push ax mov ax, 7Bh ; 123 push ax call func3 xor ax, ax ; return 0 pop bp retn 0Ah WinMain endp ``` 32位的值(long數據類型代表32位,int代表16位數據)在16位模式下(MSDOS和win16)都會按對傳遞,就像64位數據在32位環境下使用的方式一樣(21章)。 Sub_B2在這里是一個編譯器生成的庫函數,他的作用是“long乘法”,例如兩個32位類型想成,其他的編譯器函數列在了附錄E, D.中。 ADD/ADC指令對用來相加兩個值:ADD將設置/清空CF進位標識,ADC將會使用它。 SUB/SBB將會做減法,SUB會設置/清空CF標識位,SBB將會使用它。 32位值按照DX:AX寄存器對返回。 常數同樣在WinMain()中按照值對的方式傳遞。 Int類型的123常量首先被轉為32位的值,使用的是CWD指令。 ## 30.5 例子#5 ``` #include <windows.h> int PASCAL string_compare (char *s1, char *s2) { while (1) { if (*s1!=*s2) return 0; if (*s1==0 || *s2==0) return 1; // end of string s1++; s2++; }; }; int PASCAL string_compare_far (char far *s1, char far *s2) { while (1) { if (*s1!=*s2) return 0; if (*s1==0 || *s2==0) return 1; // end of string s1++; s2++; }; }; void PASCAL remove_digits (char *s) { while (*s) { if (*s>=’0’ && *s<=’9’) *s=’-’; s++; }; }; char str[]="hello 1234 world"; int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { string_compare ("asd", "def"); string_compare_far ("asd", "def"); remove_digits (str); MessageBox (NULL, str, "caption", MB_YESNOCANCEL); return 0; }; string_compare proc near arg_0 = word ptr 4 arg_2 = word ptr 6 push bp mov bp, sp push si mov si, [bp+arg_0] mov bx, [bp+arg_2] loc_12: ; CODE XREF: string_compare+21j mov al, [bx] cmp al, [si] jz short loc_1C xor ax, ax jmp short loc_2B ; --------------------------------------------------------------------------- loc_1C: ; CODE XREF: string_compare+Ej test al, al jz short loc_22 jnz short loc_27 loc_22: ; CODE XREF: string_compare+16j mov ax, 1 jmp short loc_2B ; --------------------------------------------------------------------------- loc_27: ; CODE XREF: string_compare+18j inc bx inc si jmp short loc_12 ; --------------------------------------------------------------------------- loc_2B: ; CODE XREF: string_compare+12j ; string_compare+1Dj pop si pop bp retn 4 string_compare endp string_compare_far proc near ; CODE XREF: WinMain+18p arg_0 = word ptr 4 arg_2 = word ptr 6 arg_4 = word ptr 8 arg_6 = word ptr 0Ah push bp mov bp, sp push si mov si, [bp+arg_0] mov bx, [bp+arg_4] loc_3A: ; CODE XREF: string_compare_far+35j mov es, [bp+arg_6] mov al, es:[bx] mov es, [bp+arg_2] cmp al, es:[si] jz short loc_4C xor ax, ax jmp short loc_67 ; --------------------------------------------------------------------------- loc_4C: ; CODE XREF: string_compare_far+16j mov es, [bp+arg_6] cmp byte ptr es:[bx], 0 jz short loc_5E mov es, [bp+arg_2] cmp byte ptr es:[si], 0 jnz short loc_63 loc_5E: ; CODE XREF: string_compare_far+23j mov ax, 1 jmp short loc_67 ; --------------------------------------------------------------------------- loc_63: ; CODE XREF: string_compare_far+2Cj inc bx inc si jmp short loc_3A ; --------------------------------------------------------------------------- loc_67: ; CODE XREF: string_compare_far+1Aj ; string_compare_far+31j pop si pop bp retn 8 string_compare_far endp remove_digits proc near ; CODE XREF: WinMain+1Fp arg_0 = word ptr 4 push bp mov bp, sp mov bx, [bp+arg_0] loc_72: ; CODE XREF: remove_digits+18j mov al, [bx] test al, al jz short loc_86 cmp al, 30h ; ’0’ jb short loc_83 cmp al, 39h ; ’9’ ja short loc_83 mov byte ptr [bx], 2Dh ; ’-’ loc_83: ; CODE XREF: remove_digits+Ej ; remove_digits+12j inc bx jmp short loc_72 ; --------------------------------------------------------------------------- loc_86: ; CODE XREF: remove_digits+Aj pop bp retn 2 remove_digits endp WinMain proc near ; CODE XREF: start+EDp push bp mov bp, sp mov ax, offset aAsd ; "asd" push ax mov ax, offset aDef ; "def" push ax call string_compare push ds mov ax, offset aAsd ; "asd" push ax push ds mov ax, offset aDef ; "def" push ax call string_compare_far mov ax, offset aHello1234World ; "hello 1234 world" push ax call remove_digits xor ax, ax push ax push ds mov ax, offset aHello1234World ; "hello 1234 world" push ax push ds mov ax, offset aCaption ; "caption" push ax mov ax, 3 ; MB_YESNOCANCEL push ax call MESSAGEBOX xor ax, ax pop bp retn 0Ah WinMain endp ``` 我們可以看到所謂的“near”指針和“far”指針:另一個奇怪的16位8086現象。 可以在70章繼續讀到相關內容。 近指針就是那些指向當前數據段內的指針。因為,string_compare()函數僅僅用到2個16位指針,而且訪問數據通過DS指向了它(mov al, es:[bx])。遠指針也同樣在我的16位MessageBox()例子里面:見30.2節。 因此,在訪問文本時,Windows內核并不關心使用那個數據段,所以它需要更完整的信息。 使用這種區別的原因可能是因為緊湊的程序可能使用僅僅一個64kb的數據段。所以他并不需要傳遞地址的高位數據,因為它們永遠是不變的。大一點的程序可能會使用多個64kb數據段,所以它們每次操作都需要需要區分它們是在哪個數據段里面。 對代碼段來說也是相同的故事,比較短小的程序可能在64k的數據段里面包含有所有的可執行代碼,然后所有的函數都會由CALL NEAR來調用,代碼使用RETN返回。但是,如果有多個代碼段的話,函數地址就會按對區分,然后使用CALL FAR來調用,代碼會使用RETF返回。 這就是在編譯器中指定“內存模型”會發生的事情。 MS-DOS和Win16編譯器針對每個內存模型都有有特別的庫:它們會因為數據和代碼的不同的指針模型而不同。 ## 30.6 例子#6 ``` #include <windows.h> #include <time.h> #include <stdio.h> char strbuf[256]; int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { struct tm *t; time_t unix_time; unix_time=time(NULL); t=localtime (&unix_time); sprintf (strbuf, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); MessageBox (NULL, strbuf, "caption", MB_OK); return 0; }; WinMain proc near var_4 = word ptr -4 var_2 = word ptr -2 push bp mov bp, sp push ax push ax xor ax, ax call time_ mov [bp+var_4], ax ; low part of UNIX time mov [bp+var_2], dx ; high part of UNIX time lea ax, [bp+var_4] ; take a pointer of high part call localtime_ mov bx, ax ; t push word ptr [bx] ; second push word ptr [bx+2] ; minute push word ptr [bx+4] ; hour push word ptr [bx+6] ; day push word ptr [bx+8] ; month mov ax, [bx+0Ah] ; year add ax, 1900 push ax mov ax, offset a04d02d02d02d02 ; "%04d-%02d-%02d %02d:%02d:%02d" push ax mov ax, offset strbuf push ax call sprintf_ add sp, 10h xor ax, ax ; NULL push ax push ds mov ax, offset strbuf push ax push ds mov ax, offset aCaption ; "caption" push ax xor ax, ax ; MB_OK push ax call MESSAGEBOX xor ax, ax mov sp, bp pop bp retn 0Ah WinMain endp ``` UNIX時間是32位的,所以它返回在DX:AX寄存器對中,而且將他們存儲到兩個本地16位變量中。然后一個指向值對的指針會被當作參數傳給localtime()函數。Localtime()函數有一個struct tm,它將通過C庫分配內存,所以只有指向它的指針返回了。順便一提,這也意味著在它的結果被使用之前,函數不能被再次調用。 對time()和localtime()兩個函數來說,Watcom調用轉換將會在這里:前四個參數使用AX、DX、BX、CX傳遞,剩余的通過棧來傳遞。使用這個轉換的函數也會在名字最后使用下劃線來標記。 Sprintf()并不使用PASCAL調用轉換,也不會使用watcom轉換,所以參數將使用尋常的cdecl方式傳遞(47.1節)。 **30.6.1 全局變量** 這里用同樣的例子,但是變量是全局變量: ``` #include <windows.h> #include <time.h> #include <stdio.h> char strbuf[256]; struct tm *t; time_t unix_time; int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { unix_time=time(NULL); t=localtime (&unix_time); sprintf (strbuf, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); MessageBox (NULL, strbuf, "caption", MB_OK); return 0; }; unix_time_low dw 0 unix_time_high dw 0 t dw 0 WinMain proc near push bp mov bp, sp xor ax, ax call time_ mov unix_time_low, ax mov unix_time_high, dx mov ax, offset unix_time_low call localtime_ mov bx, ax mov t, ax ; will not be used in future... push word ptr [bx] ; seconds push word ptr [bx+2] ; minutes push word ptr [bx+4] ; hour push word ptr [bx+6] ; day push word ptr [bx+8] ; month mov ax, [bx+0Ah] ; year add ax, 1900 push ax mov ax, offset a04d02d02d02d02 ; "%04d-%02d-%02d %02d:%02d:%02d" push ax mov ax, offset strbuf push ax call sprintf_ add sp, 10h xor ax, ax ; NULL push ax push ds mov ax, offset strbuf push ax push ds mov ax, offset aCaption ; "caption" push ax xor ax, ax ; MB_OK push ax call MESSAGEBOX xor ax, ax ; return 0 pop bp retn 0Ah WinMain endp ``` T不會被使用,但是編譯器還是用代碼存儲了這個值。因為他并不確定,也許這個值會在某個地方被用到。 第二部分 C++
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看