Author Topic: Matrice operation (v1[x] = v[0][x] - v[1][x])  (Read 369 times)

TouEnMasm

• Member
• Posts: 1805
Matrice operation (v1[x] = v[0][x] - v[1][x])
« on: July 28, 2021, 06:21:13 PM »
Hello,
Is someone  have written macros to solve this form of operations: v1
• = v[0]
• - v[1]
•     ?

Fa is a musical note to play with CL

HSE

• Member
• Posts: 1744
• <AMD>< 7-32>
Re: Matrice operation (v1[x] = v[0][x] - v[1][x])
« Reply #1 on: July 28, 2021, 10:33:30 PM »
Hi Yves!

Using macros in mtx.inc : http://masm32.com/board/index.php?topic=8398.0. Requiere JWasm family because there are macros creating macros.

Using Matrix.inc in ObjAsm: http://masm32.com/board/index.php?topic=8583.0.  Examples of use are in ObjAsm32. This Matrix.inc is a modification in progress of that.

A litle more specific and in development, perhaps using a more advanced mtx.inc (I have to see ). Also in ObjAsm I'm translating some C++ code:
Code: (Matri2D_OOP.inc) [Select]
`;/*!; * \file; * Scanline edge-flag algorithm for antialiasing <br>; * Copyright (c) 2005-2007 Kiia Kallio; *; * http://mlab.uiah.fi/~kkallio/antialiasing/; * ; * This code is distributed under the three-clause BSD license.; * Read the LICENSE file or visit the URL above for details.; *; * \brief A simple 2d matrix class.; *; *; * \$Id: \$; * \$Date: \$; * \$Revision: \$; */MATRIX2D textequ <POINTER> defMATRIX2D macro var1 DefineVariable &var1, MATRIX2D, 0 endm;//! A simple 2d matrix class.Object Matrix2D,, Primer    ; Makes a rotation matrix.    ;void makeRotation(RATIONAL aAngle);    VirtualMethod makeRotation, RATIONAL    ; Makes a translation matrix.    ;void makeTranslation(const Vector2d &aTranslation);    VirtualMethod makeTranslation, POINTER        ; Makes a scaling matrix.    ;void makeScaling(const Vector2d &aScale);    VirtualMethod makeScaling, POINTER    ; Makes an identity matrix.    ;void makeIdentity();    VirtualMethod makeIdentity    ; Rotates the matrix by an angle    ;void rotate(RATIONAL rotate);    VirtualMethod rotate, RATIONAL    ; Translates the matrix with a vector.    ;void translate(const Vector2d &aTranslation);    VirtualMethod translate, POINTER    ; Scales the matrix with a vector.    ;void scale(const Vector2d &aScale);    VirtualMethod scale, POINTER    ; Multiplies the matrix with another matrix.    ;void multiply(const Matrix2d &aMatrix);    VirtualMethod multiply, POINTER    ; Transforms a point with the matrix.    ;void transform(const Vector2d &aSource, Vector2d &aTarget) const;    VirtualMethod transform, POINTER, POINTER    DIMOA mMatrix, RATIONAL, 3, 2    ObjectEnd;#define MATRIX2D_MAKEROTATION(aMatrix,aAngle) aMatrix.makeRotation(aAngle);#define MATRIX2D_MAKETRANSLATION(aMatrix,aTranslation) aMatrix.makeTranslation(aTranslation)MATRIX2D_MAKESCALING macro aMatrix, aScale OCall &aMatrix::Matrix2D.makeScaling, &aScaleendm MATRIX2D_MAKEIDENTITY macro aMatrix OCall &aMatrix::Matrix2D.makeIdentityendm ;#define MATRIX2D_ROTATE(aMatrix,aAngle) aMatrix.rotate(aAngle)MATRIX2D_TRANSLATE macro aMatrix, aTranslation OCall &aMatrix::Matrix2D.translate, &aTranslationendm;#define MATRIX2D_SCALE(aMatrix,aScale) aMatrix.scale(aScale)MATRIX2D_MULTIPLY macro aMatrix, aMultiplier OCall &aMatrix::Matrix2D.multiply, &aMultiplierendm MATRIX2D_TRANSFORM macro aMatrix,aSource,aResult  OCall &aMatrix::Matrix2D.transform, &aSource, &aResultendm;#define MATRIX2D_GET_M11(aMatrix) aMatrix.mMatrix[0][0];#define MATRIX2D_GET_M12(aMatrix) aMatrix.mMatrix[0][1];#define MATRIX2D_GET_M21(aMatrix) aMatrix.mMatrix[1][0];#define MATRIX2D_GET_M22(aMatrix) aMatrix.mMatrix[1][1];#define MATRIX2D_GET_DX(aMatrix) aMatrix.mMatrix[2][0];#define MATRIX2D_GET_DY(aMatrix) aMatrix.mMatrix[2][1]`
Code: (Matrix2D_PROC.inc) [Select]
`; * Scanline edge-flag algorithm for antialiasing <br>; * Copyright (c) 2005-2007 Kiia Kallio; *; * http:;mlab.uiah.fi/~kkallio/antialiasing/; * ; * This code is distributed under the three-clause BSD license.; * Read the LICENSE file or visit the URL above for details.; *; * \brief A simple 2d matrix class.; Makes a rotation matrix.Method Matrix2D.makeRotation, uses xsi, aAngle : RATIONAL local sinRot : RATIONAL local cosRot : RATIONAL SetObject xsi        fSlv8 sinRot = sin(aAngle)    fSlv8 cosRot = cos(aAngle)    fSlv8 mMatrix(0,0) = cosRot    fSlv8 mMatrix(0,1) = sinRot    fSlv8 mMatrix(1,0) = -sinRot    fSlv8 mMatrix(1,1) = cosRot    fSlv8 mMatrix(2,0) = 0    fSlv8 mMatrix(2,1) = 0    MethodEnd; Makes a translation matrix.Method Matrix2D.makeTranslation, uses xsi, aTranslation : POINTER SetObject xsi SetObject xdx, Vector2D, aTranslation     fSlv8 mMatrix(0,0) = 1    fSlv8 mMatrix(0,1) = 0    fSlv8 mMatrix(1,0) = 0    fSlv8 mMatrix(1,1) = 1    fSlv8 mMatrix(2,0) = [xdx].mX    fSlv8 mMatrix(2,1) = [xdx].mYMethodEnd; Makes a scaling matrix.Method Matrix2D.makeScaling, uses xsi, aScale :POINTER    SetObject xsi    SetObject xdx, Vector2D, aScale        fSlv8 mMatrix(0,0) = [xdx].mX;    fSlv8 mMatrix(0,1) = 0    fSlv8 mMatrix(1,0) = 0    fSlv8 mMatrix(1,1) = [xdx].mY;    fSlv8 mMatrix(2,0) = 0    fSlv8 mMatrix(2,1) = 0    MethodEnd; Makes an identity matrix.Method Matrix2D.makeIdentity, uses xsi    SetObject xsi        fSlv8 mMatrix(0,0) = 1    fSlv8 mMatrix(0,1) = 0    fSlv8 mMatrix(1,0) = 0    fSlv8 mMatrix(1,1) = 1    fSlv8 mMatrix(2,0) = 0    fSlv8 mMatrix(2,1) = 0    MethodEnd; Rotates the matrix by an angleMethod Matrix2D.rotate, uses xsi, rotate : RATIONAL local tmp : POINTER     SetObject xsi    New Matrix2D    mov tmp, xax    OCall tmp::Matrix2D.makeRotation, rotate    OCall xsi.multiply, tmp    Destroy tmpMethodEnd; Translates the matrix with a vector.Method Matrix2D.translate, uses xsi, aTranslation : POINTER local tmp : POINTER     SetObject xsi ;SetObject xdx, Vector2D, aTranslation ;pointer to Vector2d    New Matrix2D    mov tmp, xax    OCall tmp::Matrix2D.makeTranslation, aTranslation    OCall xsi.multiply, tmp    Destroy tmp    MethodEnd; Scales the matrix with a vector.Method Matrix2D.scale, uses xsi, aScale : POINTER local tmp : POINTER     SetObject xsi ;Vector2d    New Matrix2D; tmp;    mov tmp, xax    OCall tmp::Matrix2D.makeScaling, aScale    OCall xsi.multiply, tmp    Destroy tmp    MethodEnd; Multiplies the matrix with another matrix.Method Matrix2D.multiply, uses xsi xdi, aMatrix : POINTER local tmp : POINTER SetObject xsi New Matrix2D    mov tmp, eax        CopiaObjeto OA_Matrix2D, tmp    SetObject xdi, Matrix2D, aMatrix SetObject xdx, Matrix2D, tmp        fSlv8 mMatrix(0,0) = mMatrixP(xdx,0,0) * mMatrixP(xdi,0,0) +\                    mMatrixP(xdx,0,1) * mMatrixP(xdi,1,0);    fSlv8 mMatrix(0,1) = mMatrixP(xdx,0,0) * mMatrixP(xdi,0,1) +\                    mMatrixP(xdx,0,1) * mMatrixP(xdi,1,1);    fSlv8 mMatrix(1,0) = mMatrixP(xdx,1,0) * mMatrixP(xdi,0,0) +\                    mMatrixP(xdx,1,1) * mMatrixP(xdi,1,0);    fSlv8 mMatrix(1,1) = mMatrixP(xdx,1,0) * mMatrixP(xdi,0,1) +\                    mMatrixP(xdx,1,1) * mMatrixP(xdi,1,1);    fSlv8 mMatrix(2,0) = mMatrixP(xdx,2,0) * mMatrixP(xdi,0,0) +\                    mMatrixP(xdx,2,1) * mMatrixP(xdi,1,0) +\                    mMatrixP(xdi,2,0);    fSlv8 mMatrix(2,1) = mMatrixP(xdx,2,0) * mMatrixP(xdi,0,1) +\                    mMatrixP(xdx,2,1) * mMatrixP(xdi,1,1) +\                    mMatrixP(xdi,2,1);   DbgFloat mMatrix(0,0)                    DbgFloat mMatrix(0,1)                    DbgFloat mMatrix(1,0)                    DbgFloat mMatrix(1,1)                    DbgFloat mMatrix(2,0)                    DbgFloat mMatrix(2,1)                              ;Ç    Destroy tmp             MethodEnd; Transforms a point with the matrix.Method Matrix2D.transform, uses xsi xdi, aSource : POINTER, aTarget :POINTER local vector2D : \$Obj(Vector2D) DbgMtit    SetObject xsi SetObject xdi, Vector2D, aSource DbgHex xdi DbgHex aTarget .if xdi == aTarget lea xax, vector2D SetObject xdx, Vector2D, xax .else SetObject xdx, Vector2D, aTarget .endif DbgHex xdx    fSlv8 [xdx].mX = mMatrix(0,0) * [xdi].mX + mMatrix(1,0) * [xdi].mY + mMatrix(2,0) fSlv8 [xdx].mY = mMatrix(0,1) * [xdi].mX + mMatrix(1,1) * [xdi].mY + mMatrix(2,1) .if xdi == aTarget lea xax, vector2D SetObject xdi, Vector2D, xax SetObject xdx, Vector2D, aTarget fSlv8 [xdx].mX = [xdi].mX fSlv8 [xdx].mY = [xdi].mY .endif DbgMsalMethodEnd`
Yes. To include matrix in objects  requiere this:
Code: (mtx.inc) [Select]
`;* @ArgI - Macro function returns an argument specified by number;* from a VARARG list.;*;* Params:  index - one-based number of the argument to be returned;*          arglist - argument list@ArgI MACRO index:REQ, arglist:VARARG    LOCAL count,retstr    count = 0    FOR arg,<arglist>        count = count + 1        IF count EQ index            retstr TEXTEQU <arg>        ENDIF    ENDM    EXITM retstrENDM;mtx_tls_warning_line = -1;@mtxTLS_warning macro; IF mtx_tls_warning_line NE @Line; %echo @FileCur: WARNING: code not thread save: line @CatStr(%@Line); ENDIF; mtx_tls_warning_line = @Line;endmmtxTLS_curr_line = -1mtxTLS_curr_file textequ <>; mod1 0 = variable, 1 = structure; mod2 0 = standard, 1 = SmplMath; mod3 0 = plain, 1 = ObjAsmDIM_MASTER macro isStruct, isSmpMth, isOA, nombre, tipo, dimensions:VARARG   local value1   value1 textequ <1>   count = 0    FOR dimension, <&dimensions>        value1 CATSTR value1 , <*>, %dimension    ENDM    if isOA eq 0     if isStruct eq 0         nombre&_mtx &tipo value1 dup (?)     else         nombre&_mtx &tipo value1 dup (<?>)     endif    else    nombre&_mtx_type textequ <&tipo>     if isStruct eq 0         DefineVariable nombre&_mtx, &tipo, value1 dup (?)     else         DefineVariable nombre&_mtx, &tipo, value1 dup (<?>)     endif    endif    &nombre MACRO dimensionsn:VARARG        LOCAL count, dwidth, dist, hayc, hayv, dewidth2, nombrestore        count = 0        dwidth = 1        dist = 0        hayc = 0        hayv = 0        if 0            if &isSmpMth eq 1        IF mtxTLS_curr_line NE @Line        mtxTLS_curr_line = @Line        mtxTLS_usados = 0        ENDIF        endif        endif        FOR arg,<&dimensionsn>            oa = OPATTR arg            if oa and 0100b                hayc = 1            else                hayv = 1            endif        ENDM        if hayv            mov eax, 0        endif        FOR arg,<&dimensionsn>            count = count + 1            oa = OPATTR arg            if oa and 0100b            if isOA eq 0                dwidth2 = dwidth * TYPE nombre&_mtx                else                dwidth2 = dwidth * TYPE nombre&_mtx_type                endif                dist = dist + (arg * dwidth2)            else                push eax                mov eax, arg                mov ecx, dwidth                mul ecx                        pop ecx                add eax, ecx            endif            dwidth = dwidth * @ArgI(%count, <dimensions>)         ENDM        if hayv             if isStruct eq 1             if isOA eq 0                mov ecx,  TYPE nombre&_mtx                else                mov ecx,  TYPE nombre&_mtx_type                endif                mul ecx            endif            if isSmpMth eq 1             %   add eax, offset @CatStr(&nombre,<_mtx>)                mtxTLS_usados = mtxTLS_usados + 1           ;;     % echo @CatStr(%mtxTLS_usados)           %     if mtxTLS_usados gt mtxTLS_reservados                    .err   incrementar reserva mtxTLS() en @FileCur                endif                if hayc                %    add eax, dist                endif           %     mov dword ptr SS:[ebp - mtxTLS_usados*@WordSize ], eax            endif        endif        if hayv and hayc            if isStruct eq 0                    if isSmpMth eq 0                if isOA eq 0                    EXITM @CatStr(&nombre,<_mtx>,<[>,eax * TYPE nombre&_mtx+ %dist ,<]>)                    else                    EXITM @CatStr(<[esi].>,&nombre,<_mtx>,<[>,eax * TYPE nombre&_mtx_type+ %dist ,<]>)                    endif                 else                   % echo @CatStr(<add dword ptr [ebp - >,%mtxTLS_usados*@WordSize,< ],>, %dist)                   % @CatStr(<add dword ptr SS:[ebp - >,%mtxTLS_usados*@WordSize,< ],>, %dist)     % echo                   EXITM <@CatStr(tipo,< ptr [ebp->,%mtxTLS_usados*@WordSize,<]>)>                    EXITM <@CatStr(tipo,< ptr [ebp->,%mtxTLS_usados*@WordSize,<]>)>                endif            else                if isSmpMth eq 0                    EXITM @CatStr(&nombre,<_mtx>,<[>,eax + %dist ,<]>)                else                    add dword ptr SS:[ebp - mtxTLS_usados*@WordSize ], %dist                    EXITM <@CatStr(<[ebp->,mtxTLS_usados*@WordSize,<].>,&tipo)>                endif            endif        elseif hayv            if isStruct eq 0                    if isSmpMth eq 0                if isOA eq 0                    EXITM @CatStr(&nombre,<_mtx>,<[>,eax * TYPE nombre&_mtx ,<]>)                    else                    EXITM @CatStr(<[esi].>,&nombre,<_mtx>,<[>,eax * TYPE nombre&_mtx_type ,<]>)                    endif                else                    EXITM <@CatStr(&nombre,<_mtx>,<[ebp->,mtxTLS_usados*@WordSize,<]>)>                endif            else                if isSmpMth eq 0                if isOA eq 0                    EXITM @CatStr(&nombre,<_mtx>,<[>,eax,<]>)                    else                    EXITM @CatStr(<[esi].>,&nombre,<_mtx>,<[>,eax,<]>)                    endif                else                    EXITM <@CatStr(<[ebp->,mtxTLS_usados*@WordSize,<].>,&tipo)>                endif            endif        elseif hayc        if isOA eq 0            EXITM @CatStr(&nombre,<_mtx>,<[>, %dist ,<]>)            else                EXITM @CatStr(<[esi].>,&nombre,<_mtx>,<[>, %dist ,<]>) endif        endif    ENDM ;-------------------------------------------------------------------------------     &nombre&P MACRO PuntReg:=<esi>, dimensionsn:VARARG        LOCAL count, dwidth, dist, hayc, hayv, dewidth2, nombrestore        count = 0        dwidth = 1        dist = 0        hayc = 0        hayv = 0        if 0            if &isSmpMth eq 1        IF mtxTLS_curr_line NE @Line        mtxTLS_curr_line = @Line        mtxTLS_usados = 0        ENDIF        endif        endif        FOR arg,<&dimensionsn>            oa = OPATTR arg            if oa and 0100b                hayc = 1            else                hayv = 1            endif        ENDM        if hayv            mov eax, 0        endif        FOR arg,<&dimensionsn>            count = count + 1            oa = OPATTR arg            if oa and 0100b            if isOA eq 0                dwidth2 = dwidth * TYPE nombre&_mtx                else                dwidth2 = dwidth * TYPE nombre&_mtx_type                endif                dist = dist + (arg * dwidth2)            else                push eax                mov eax, arg                mov ecx, dwidth                mul ecx                        pop ecx                add eax, ecx            endif            dwidth = dwidth * @ArgI(%count, <dimensions>)         ENDM        if hayv             if isStruct eq 1             if isOA eq 0                mov ecx,  TYPE nombre&_mtx                else                mov ecx,  TYPE nombre&_mtx_type                endif                mul ecx            endif            if isSmpMth eq 1             %   add eax, offset @CatStr(&nombre,<_mtx>)                mtxTLS_usados = mtxTLS_usados + 1           ;;     % echo @CatStr(%mtxTLS_usados)           %     if mtxTLS_usados gt mtxTLS_reservados                    .err   incrementar reserva mtxTLS() en @FileCur                endif                if hayc                %    add eax, dist                endif           %     mov dword ptr SS:[ebp - mtxTLS_usados*@WordSize ], eax            endif        endif        if hayv and hayc            if isStruct eq 0                    if isSmpMth eq 0                if isOA eq 0                    EXITM @CatStr(&nombre,<_mtx>,<[>,eax * TYPE nombre&_mtx+ %dist ,<]>)                    else                    EXITM @CatStr(<[>,&PuntReg,<].>,&nombre,<_mtx>,<[>,eax * TYPE nombre&_mtx_type+ %dist ,<]>)                    endif                 else                   % echo @CatStr(<add dword ptr [ebp - >,%mtxTLS_usados*@WordSize,< ],>, %dist)                   % @CatStr(<add dword ptr SS:[ebp - >,%mtxTLS_usados*@WordSize,< ],>, %dist)     % echo                   EXITM <@CatStr(tipo,< ptr [ebp->,%mtxTLS_usados*@WordSize,<]>)>                    EXITM <@CatStr(tipo,< ptr [ebp->,%mtxTLS_usados*@WordSize,<]>)>                endif            else                if isSmpMth eq 0                    EXITM @CatStr(&nombre,<_mtx>,<[>,eax + %dist ,<]>)                else                    add dword ptr SS:[ebp - mtxTLS_usados*@WordSize ], %dist                    EXITM <@CatStr(<[ebp->,mtxTLS_usados*@WordSize,<].>,&tipo)>                endif            endif        elseif hayv            if isStruct eq 0                    if isSmpMth eq 0                if isOA eq 0                    EXITM @CatStr(&nombre,<_mtx>,<[>,eax * TYPE nombre&_mtx ,<]>)                    else                    EXITM @CatStr(<[>,&PuntReg,<].>,&nombre,<_mtx>,<[>,eax * TYPE nombre&_mtx_type ,<]>)                    endif                else                    EXITM <@CatStr(&nombre,<_mtx>,<[ebp->,mtxTLS_usados*@WordSize,<]>)>                endif            else                if isSmpMth eq 0                if isOA eq 0                    EXITM @CatStr(&nombre,<_mtx>,<[>,eax,<]>)                    else                    EXITM @CatStr(<[>,&PuntReg,<].>,&nombre,<_mtx>,<[>,eax,<]>)                    endif                else                    EXITM <@CatStr(<[ebp->,mtxTLS_usados*@WordSize,<].>,&tipo)>                endif            endif        elseif hayc        if isOA eq 0            EXITM @CatStr(&nombre,<_mtx>,<[>, %dist ,<]>)            else                EXITM @CatStr(<[>,&PuntReg,<].>,&nombre,<_mtx>,<[>, %dist ,<]>) endif        endif    ENDMendmDIM macro nombre, tipo, dimensions:VARARG%    DIM_MASTER 0, 0, 0, nombre, tipo, <dimensions>ENDMDIMS macro nombre, tipo, dimensions:VARARG%    DIM_MASTER 1, 0, 0, nombre, tipo, <dimensions>ENDMDIMOA macro nombre, tipo, dimensions:VARARG%    DIM_MASTER 0, 0, 1, nombre, tipo, <dimensions>ENDMDIMSOA macro nombre, tipo, dimensions:VARARG%    DIM_MASTER 0, 0, 1, nombre, tipo, <dimensions>ENDMmtxTLS macro punteros    ifb <punteros>    %    mtxTLS_reservados = 4*@WordSize    else    %    mtxTLS_reservados = &punteros*@WordSize    endif    mtxTLS_usados = 0    %EXITM <mtxTLS_puntero[mtxTLS_reservados]:byte>endm`
Regards, HSE.

daydreamer

• Member
• Posts: 1721
• building nextdoor
Re: Matrice operation (v1[x] = v[0][x] - v[1][x])
« Reply #2 on: July 30, 2021, 02:38:27 AM »
Yves
I expected the kind of matrice masm macro you use, when cpu don't support dotproduct
D3d matrices library
SIMD fan and macro fan
why assembly is fastest is because its switch has no (brakes) breaks
:P
only in 16bit assembly you can get away with "Only words" :P

TouEnMasm

• Member
• Posts: 1805
Re: Matrice operation (v1[x] = v[0][x] - v[1][x])
« Reply #3 on: July 30, 2021, 05:27:02 PM »

I search Macro to do it.My prefered method is to let it done by the c compiler (mixed langage c asm).
Perhap's smplmath is abble to do it but i need a demo.
After stay to write macro to do that.

Fa is a musical note to play with CL

HSE

• Member
• Posts: 1744
• <AMD>< 7-32>
Re: Matrice operation (v1[x] = v[0][x] - v[1][x])
« Reply #4 on: July 30, 2021, 10:39:26 PM »
I search Macro to do it

Only macros in Mtx.inc

Perhap's smplmath is abble to do it but i need a demo.

It's not SmplMath and, because macro expansion, only work with SmplMath in limited cases. Of course, is very interesting when can work together, how you can see in some Hydrograph lines.

Don't need SmplMath:
Code: [Select]
` fild v2                        fmul varD                         fsub descargaunidad(v1ant, 0)            fld descargaunidad(v1, 0)                 fsub descargaunidad(v1ant, 0)                 fdiv                 fld descargaunidad(v1, 1)                 fsub descargaunidad(v1ant, 1)         fmul                 fadd descargaunidad(v1ant, 1) fstp tablero2(v2, 0)`In this case I'm using real8, but you can use any type or structure.

··············································································

If you have no mind for this advanced matrix  (or you are using ML), you still can use the 20 years old method  :
Code: [Select]
`        matrix macro p1 , p2 , p3, regaux, no1            mov  eax ,  p3            imul p1            add  eax , p2            ifb <no1>                 ifnb <regaux>                    mov  &regaux , eax                else                    mov  ebx , eax                endif            endif        endm`
Code: [Select]
`.data            mat_ET1 dd 100 dup (0)                mat_ATL_max equ 3            mat_ATL real4 3*100 dup (0.0)            mat_UTL_max equ 4            mat_UTL real4 4*100 dup (0.0).code            ···            fild mat_ET1[ecx*4]            matrix nn, 0, mat_ATL_max            fst real4 ptr mat_ATL[ebx*4]            fstp real4 ptr mat_UAT[ebx*4]            ···`This is used in now corrected http://masm32.com/board/index.php?topic=9439.msg103363#new

Regards, HSE