2012-01-10 65 views
3

當我嘗試寫鷺算法來計算來自ECX寄存器的sqrt時,它不起作用。看起來問題是將浮點數分開,因爲結果是整數。如何在x86彙編中劃分浮點數?

我的算法:

sqrtecx: 
MOV EDX, 10 ; loop count 
MOV EAX, 5 ; x_0 in heron algorythm 
MOV DWORD[EBP-100], ECX ; save INPUT (ecx is input)  
MOV DWORD[EBP-104], EDX ; save loop count 
jmp  loop 
MOV  ECX, EAX ; move OUTPUT to ECX 

loop: 

MOV DWORD[EBP-104], EDX ; save loop count 
xor edx, edx 

MOV ECX, EAX 
MOV  EAX, DWORD[EBP-100] 
DIV ECX 
ADD EAX, ECX 
XOR EDX, EDX 
mov ecx, 2 
DIV ecx 

MOV EDX, DWORD[EBP-104] ; load loop count 
DEC EDX 
JNZ loop 
+0

順便提一下,FPU代碼和SSE代碼都有一個平方根指令。所以你甚至不需要這個.. – harold 2012-01-10 14:32:57

+0

@harold,在nasm組件中有一個平方根的指示?我沒有在我的CodeTable中。你能告訴我嗎? – 2012-01-10 15:24:57

+1

用於FPU代碼的FSQRT(D9 FA),用於SSE的SQRTSS(F3 0F 51/r)和用於SSE2的SQRTSD(F2 0F 51/r)(也有帶4個打包漂浮物或2個打包雙打的版本)。下面是一個更完整的參考:http://siyobik.info/main/reference/ – harold 2012-01-10 15:46:18

回答

5

DIV是整數除法 - 你需要FDIV浮點(或更可能FIDIV在這種特殊情況下,因爲它看起來像你開始用一個整數值)。

+0

當我編輯DIV,FiDIV或FDIV它不起作用。可能我做錯了。你確定它在nasm中工作嗎? FFTCount32.S:188:錯誤:操作碼的組合無效和操作數 FFTCount32.S:192:錯誤:操作碼的組合無效和操作數 – 2012-01-10 15:23:18

8

您需要使用浮點指令集來實現您的目標。你可能會發現一些有用的指令是:

fild <int> - loads and integer into st0 (not an immediate 
faddp  - adds st0 to st1, and pop from reg stack (i.e. result in st0) 
fdivp  - divides st0 by st1, then pop from reg stack (again, result in st0) 

這裏有一個簡單的例子片斷(VS2010聯彙編):

int main(void) 
{ 
    float res; 

    __asm { 
     push dword ptr 5;  // fild needs a memory location, the trick is 
     fild [esp];   // to use the stack as a temp. storage 
     fild [esp];   // now st0 and st1 both contain (float) 5 
     add  esp, 4;   // better not screw up the stack 
     fadd st(0), st(0); // st0 = st0 + st0 = 10 
     fdivp st(1), st(0); // st0 = st1/st0 = 5/10 = 0.5 
     sub  esp, 4;   // again, let's make some room on the stack 
     fstp [esp];   // store the content of st0 into [esp] 
     pop  eax;    // get 0.5 off the stack 
     mov  res, eax;  // move it into res (main's local var) 
     add  esp, 4;   // preserve the stack 
    } 

    printf("res is %f", res); // write the result (0.5) 
} 

編輯:
正如哈羅德指出的那樣,也是其計算的指令直接的平方根,它是fsqrt。操作數和結果都是st0

編輯#2:
我不知道你是否真的可以加載到st0立即值作爲我reference不一樣,如果明確規定。因此,我做了一個小片段來檢查,結果是:

float res = 5.0 * 3 - 1; 
000313BE D9 05 A8 57 03 00 fld   dword ptr [[email protected] (357A8h)] 
000313C4 D9 5D F8    fstp  dword ptr [res] 

這些字節在357A8h

[email protected]: 
000357A8 00 00    add   byte ptr [eax],al 
000357AA 60     pushad 
000357AB 41     inc   ecx 

所以我不得不得出結論,不幸的是,你必須在一些地方保存你的號碼在加載和存儲它們時都在主存中。當然,按照我上面的建議使用堆棧並不是強制性的,實際上你也可以在你的數據段或其他地方定義一些變量。

編輯#3:
別擔心,組裝是一個強大的野獸打;)關於你的代碼:

mov  ecx, 169 ; the number with i wanna to root 
sub  esp, 100 ; i move esp for free space 
push ecx   ; i save value of ecx 
add  esp,4  ; push was move my ebp,then i must come back 
fld     ; i load from esp, then i should load ecx 
fsqrt    ; i sqrt it 
fst     ; i save it on ebp+100 
add  esp,100  ; back esp to ebp 

你錯過的fldfst操作數。看看你的意見,我想你想要fld [esp]fst [esp],我不明白你爲什麼談論ebpebp應該保存堆棧框架的開始(其中有很多我們不應該搞砸的東西),而esp則包含它的結尾。我們基本上希望在堆棧框架結束時進行操作,因爲在它之後只有垃圾沒有人關心。
在計算並保存平方根後,您還應該在末尾add esp, 4。這是因爲push ecx確實也在sub esp, 4之下爲您推送的價值騰出空間,並且在將價值保存回來時仍需要一些空間。只是爲了這個,你也可以避免sub esp, 100add esp, 100,因爲房間已經由push爲你製造了。
最後一個「警告」:整數和浮點值的表示方式非常不同,因此當您知道必須使用這兩種類型時,請小心所選的說明。您建議的代碼使用fldfst,它們都使用浮點值進行操作,所以得到的結果不會成爲您期望的結果。一個例子? 00 00 00 A9是169上的字節表示,但它表示浮點數+ 2.3681944047089408e-0043(對於那些挑剔的人來說它實際上是一個長整數)。
所以,最終的代碼是:

mov  ecx, 169; // the number which we wanna root 
push ecx;  // save it on the stack 
fild [esp];  // load into st0 
fsqrt;    // find the square root 
fistp [esp];  // save it back on stack (as an integer) 
// or fst [esp] for saving it as a float 
pop ecx;   // get it back in ecx 
+0

現在我understend所有。有整數寄存器和浮點數。但是,如果我在ECX中有整數,並且想要在st0中使用ECX的平方根,而不使用堆棧,我該怎麼辦?有可能不使用堆棧? 我想某事像這樣: 'MOV \t ECX,144 \t MOV \t ST0,ECX \t FSQRT' 但它不工作:( – 2012-01-10 16:41:39

+0

@MieszkoMikulski:看到我的編輯:) – BlackBear 2012-01-10 17:19:37

+0

謝謝你的下一個答案。我很開心,那麼我的理解程度就很低。當我讀到你的答案後,我寫這樣的代碼,如: 'mov \t ecx,169;我想要根號碼 \t sub \t esp,100;我移動ESP免費空間 \t推\t ecx;我保存ecx的價值 \t add \t esp,4;推動了我的ebp,然後我必須回來 \t fld \t;我從ESP加載,然後我應該加載ecx \t fsqrt \t; i sqrt it \t fst;我把它保存在ebp + 100上 \t add \t esp,100;在我看來它應該像我發表評論(在';'之後)但它不會.. – 2012-01-10 17:49:56

4

我不能完全確定你真正想做的事,所以現在我會假設你要取的整數浮點平方根。

mov dword ptr[esp],ecx ; can't load a GRP onto the FPU stack, so go through mem 
fild dword ptr[esp]  ; read it back (as integer, converted to float) 
fsqrt     ; take the square root 

第一個dword ptr可能是可選的,這取決於您的彙編程序。

在此代碼之後,結果位於FPU堆棧ST(0)的頂部。我不知道你想用事後它做什麼..如果你想將它四捨五入爲int,並把它背在ECX,我建議這樣的:

fistp dword ptr[esp]  ; again it can't go directly, it has to go through mem 
mov ecx,dword ptr[esp] 

我將在SSE2扔好辦法的方法:

cvtsi2sd xmm0,ecx ; convert int to double 
sqrtsd xmm0,xmm0 ; take the square root 
cvtsd2si ecx,xmm0 ; round back to int (cvttsd2si for truncate instead of round) 

這樣更容易一些。