Zoom Icon

Assembly Floating Point

From UIC

Assembly Floating Point

Contents


Assembly Floating Point
Author: Pn
Email: Pnmail.png
Website: No site
Date: 20/10/2007 (dd/mm/yyyy)
Level: Some skills are required
Language: Italian Flag Italian.gif
Comments:



Introduzione

Lo scopo di questo tutorial è quello di istruire all’uso dell’i80x87. Conoscere le istruzioni dell’i80x87 può essere veramente utile per il Cracking o, nel caso di un programmatore in asm, per ottenere buone prestazioni matematiche dal vostro software: ovvero una velocità 100 volte più alta rispetto al tempo di calcolo dell’i80x86.(dal tute di Byte)


Link e Riferimenti

Manuali Intel
MASM Programmer's Guide
Operazioni a carte e penna con i numeri mobili
Decimal to Floating-Point Conversions
Masm 9


Essay

Iniziamo il discorso sull'uso dei Floating Point in assembly, spiegando che come è rappresentato un numero floating point secondo lo standard IEEE 754

Floating Point numbers

Un numero floating-point è composto da:

IMG NUM.GIF
  • Segno rappresentato da un bit. Se impostato a 1 il numero è negativo, a 0 è positivo
  • Mantissa (Significand) codificata in Normalized Numbers e suddivisa in:
    • virgola (Integer Bit o J-Bit)
    • Frazione
  • Esponente codificato nella Biased Form. B è la base

La mantissa è normalizzata (normalized) quando prima della virgola vi è solo un intero
esempio: 106.25 x 102 normalizzato diventa 1.0625 x 104 (qui la base è 10)

L'esponente è rappresentato sempre polarizzato (biased): cioè al valore reale viene aggiunta una costante, data da: 2k-1-1 dove k è il numero di bit usati per rappresentare l'esponente

Floating Point Data Types

IEEE standard 754 ha definito tre tipi di Floating Point data type per l'uso sui calcolatori:

  1. single precision (4 byte)
  2. double precision (8 byte)
  3. double-extended precision (10 byte)

La tabella sottostante mostra lunghezza, precisione e il range di ogni floating point type.

Float precision.jpg

Solo nel double-extended f-p, la virgola è memorizzata in un apposito bit: il 63esimo, negli altri due tipi essa è implicita nella parte frazionaria.

Binary fp format.jpg

Usando questo formato sono rappresentabili dei valori speciali quali:

  • -0 e +0
  • +∞ e -∞
  • QNan e SNan

ed i numeri finiti, diversi da zero,che sono suddivisi in:

  • Normalized: tutti quei numeri finiti diversi da zero, che vanno da 0 a ∞
  • Denormalized: un numero è denormalized quando l'esponente è zero, e la rappresentazione dei numeri più piccoli, può essere fatta solo impostando la virgola a zero.

Ecco una tabella che mostra la rappresentazione dei reali nel floating-point format:

Rappre fp format.jpg


Architettura co-processore X87

X87 FPU fornisce alte performance nei calcoli basati sui numeri a virgola mobile, performance necessarie nel campo della grafica, dello sviluppo scientifico ed ingegneristico.

X87 envirnment.jpg

L'ambiente X87 FPU consiste in otto data registers (X87 FPU Data Register), e nei seguenti registri special-purpose (che non saranno spiegati in questo tutorial):

  • Status register
  • Control register
  • Tag word register
  • Last instruction pointer register
  • Last data pointer register
  • Opcode register

X87 FPU Data Registers

X87 FPU Data Registers consiste in 8 registri da 80 bit, quindi i valori saranno storati secondo il double-extended f-p format, indipendentemente dal tipo di partenza (integer format, BCD, etc..), poichè i valori saranno convertiti automaticamente.
Inoltre quando si copia una valore da un registro alla memoria(vedi il paragrafo sulle istruzioni) è possibile convertire un double-extended non solo in double/single precision f-p, ma anche in integer o BCD.

Tornado ai nostri Data Register, essi sono trattati come uno stack, come si vede dall'immagine:

Si usa l'espressione ST(x0 ≤ x ≤ 7), per indicare il registro xesimo, dal TOP dello stack, che si vuole utilizzare.

Come per i normali registri della CPU, gli X87 FPU data register non sono modificati dalle call, restano integri attraverso le procedure, se non modificati attraverso le apposite istruzioni.

Istruzioni X87

Le istruzioni che il co-processore x87 supporta, si possono raggruppare in 6 categorie:

  • Data transfer instructions
  • Basic arithmetic instructions
  • Comparison instructions
  • Transcendental instructions
  • Load constant instructions
  • x87 FPU control instructions

Data transfer

Le operazioni per il trasferimento dati sono usate per eseguire le seguenti operazioni:

  • Caricare un numero dalla memoria a ST(0)
  • Salvare un numero da ST(0) alla memoria
  • Spostamento di valori tra due registi X87 FPU
FLD Load Floating Point
FILD Load Integer
FBLD Load Packed Decimal
FST Store Floating Point
FIST Store Integer
FSTP Store Floating Point and Pop
FISTP Store Integer and Pop
FBSTP Store Packed Decimal and Pop
FXCH Exchange Register Contents
FCMOVcc Conditional Move


Basic Arithmetic

FADD/FADDP Add floating point (and Pop the value)
FIADD Add integer to floating point
FSUB/FSUBP Subtract floating point
FISUB Subtract integer from floating point
FSUBR/FSUBRP Reverse subtract floating point
FISUBR Reverse subtract floating point from integer
FMUL/FMULP Multiply floating point
FIMUL Multiply integer by floating point
FDIV/FDIVP Divide floating point
FIDIV Divide floating point by integer
FDIVR/FDIVRP Reverse divide
FIDIVR Reverse divide integer by floating point
FABS Absolute value
FCHS Change sign
FSQRT Square root
FPREM Partial remainder
FPREM1 IEEE partial remainder
FRNDINT Round to integral value
FXTRACT Extract exponent and significand

Inoltre le quattro operazioni basi (+,-,*,/), possono effettuare ciò:

  • Operare tra due registi x87 FPU
  • Operare tra un registro X87 FPU ed un valore residente in memoria

Comparison

FCOM/FCOMP/FCOMPP Compare floating point and set x87 FPU condition code
flags.
FUCOM/FUCOMP/FUCOMPP Unordered compare floating point and set x87 FPU
condition code flags.
FICOM/FICOMP Compare integer and set x87 FPU condition code flags.
FCOMI/FCOMIP Compare floating point and set EFLAGS status flags.
FUCOMI/FUCOMIP Unordered compare floating point and set EFLAGS status
flags.
FTST Test (compare floating point with 0.0).
FXAM Examine the contest of ST(0).

Quando si esegue un confronto usando queste istruzioni, viene modificato X87 FPU Status word register, e non gli status flag nell' EFLAGS.
Per copiare X87 FPU Status word negli otto bit più bassi dell'EFLAGS register, si può effettuare la seguente procedura:

  • FSTSW AX: copia in AX lo status word register
  • SAHF: copia il valore di AX negli otto bit più bassi di EFLAGS.

A questo punto si possono usare i nostri bene amati jcc.

Trascendental

FSIN Sine
FCOS Cosine
FSINCOS Sine and cosine
FPTAN Tangent
FPATAN Arctangent
FYL2X Compute st(1)*log st(0)
FYL2XP1 Compute st(1) * log (st(0) + 1.0)
F2XM1 Compute 2^(st(0))-1
FSCALE Scale


Load constant

FLDZ Load +0.0
FLD1 Load +1.0
FLDPI Load π
FLDL2T Load log2 10
FLDL2E Load log2e
FLDLG2 Load log102
FLDLN2 Load loge2

X87 FPU Control

FINIT/FNINIT Initialize x87 FPU
FLDCW Load x87 FPU control word
FSTCW/FNSTCW Store x87 FPU control word
FSTSW/FNSTSW Store x87 FPU status word
FCLEX/FNCLEX Clear x87 FPU exception flags
FLDENV Load x87 FPU environment
FSTENV/FNSTENV Store x87 FPU environment
FRSTOR Restore x87 FPU state
FSAVE/FNSAVE Save x87 FPU state
FINCSTP Increment x87 FPU register stack pointer
FDECSTP Decrement x87 FPU register stack pointer
FFREE Free x87 FPU register
FNOP No operation
WAIT/FWAIT Check for and handle pending unmasked x87 FPU exceptions

Esempi in Assembly

Gli esempi sono basati sulle direttive del MASM.

Dichiarazione dei tipi

Il MASM ha tre parole chiavi utili per la dichiarazione dei nostri numeri reali:

  • REAL4 single precision -> 4 byte
  • REAL8 double precision -> 8 byte
  • REAL10 extende-double precision -> 10 byte


Inizializziamo la X87 FPU

Quando si utilizzano nel nostro codice asm le istruzioni X87, è di buona norma usare la seguente linea guida:

wait
finit
fld value
...
wait

...
...

wait
fld value
...
wait

WAIT è utilissimo: infatti serve a sincronizzare la CPU con la FPU
FINIT serve ad inizializzare la FPU (la setta con i valori di default)

Infine ecco due brevi esempi:

Codice per MASM/x86

.486
.model flat, stdcall

        include windows.inc
        include kernel32.inc
        include user32.inc
        includelib kernel32.lib
        includelib user32.lib

.data
        int_var dd 25
        float_var REAL4 22.4
        float_var2 REAL10 23.549
       
        Titolo db "Codice di esempio",0
        Testo1 db "minore",0   
        Testo2 db "maggiore",0

.code
        Main:
        ;masm aggiunge automaticamente l'istruzione wait
        ;prima e dopo il blocco con le istruzioni per la FPU
        finit
        fld dword ptr [float_var]  ;st(3)
        fld tbyte ptr [float_var2] ; st(2)
        fild dword ptr [int_var] ; st(1)
        fldpi //; carico pi greco st(0)
        fnop
       
        ;ora aggiungo due valori
        fadd st(1)
        fcompp  ;Compare st(0) e st(1) e poi toglie questi valori dai registi
        fstsw ax
        sahf
        jb minor
        invoke MessageBox, NULL, OFFSET Testo2,OFFSET  Titolo, 0
        jmp exit
minor:
        invoke MessageBox, NULL, OFFSET  Testo1, OFFSET Titolo, 0      
exit:
        invoke ExitProcess, NULL
       
        end Main

Codice per ml64

extrn MessageBoxA : proc
extrn ExitProcess : proc

.data
        int_var dd 25
        float_var REAL4 22.4
        float_var2 REAL10 23.549
       
        Titolo db "Codice di esempio",0
        Testo1 db "minore",0  
        Testo2 db "maggiore",0


.code
        Main proc
        sub rsp, 28h
        finit
        fld dword ptr [float_var]  ;st(3)
        fld tbyte ptr [float_var2] ; st(2)
        fild dword ptr [int_var] ; st(1)
        fldpi ; carico pi greco st(0)
        fnop
       
        ;ora aggiungo due valori
        fadd st(0), st(1)
        fcompp  ;Compare st(0) e st(1) e poi toglie questi valori dai registi
        fstsw ax
        sahf
        jb minor
        xor r9, r9
        lea r8, Titolo
        lea rdx, Testo2
        xor rcx, rcx
        call MessageBoxA
        jmp exit
minor:  
        xor r9, r9
        lea r8, Titolo
        lea rdx, Testo1
        xor rcx, rcx
        call MessageBoxA
exit:
        xor rcx,rcx
        call ExitProcess
        Main endp
end



Note Finali

Se usate il c/c++ tutte queste cose di cui ho parlato sopra, non vi serviranno affatto, siatene felici =).
Spero che questo tutorial sia utile.

Pn =)


Disclaimer

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevole e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.