Search code examples
optimizationx86delphi-7inline-assemblymicro-optimization

How can I optimize this graphic-sprite-emulation routine?


This routine is a windowed get/put-sprite emulation-routine. I'm Italian and I've not translated them. This routine is capable to flip-x-y the image before storing it in a 16 Byte aligned buffer. I omitted the precalculation routine for reasons of confidentiality. I learned thanks to the advice of Peter Cordes to fully use the LEA statement and is the first personal implementation of the ASSEMBLER 'BT' instruction:

Procedure Sub_MoveSprite; Assembler;

(* Sub-ROUTINE per MoveSprite (FlipX-FlipY).

 INPUT:

    EAX= Attributi immagine.
    EBX= Scarto X per origine.
    ECX= Quantità X di Pixel per LINEA.
    EDX= Quantità Y di linee da trasf.
    EBP= Scarto X per destinazione.
    ESI= OFFSET per origine.
    EDI= OFFSET per destinaz *)

Asm

     Push  EBP
     Push  EBX
     Push  ECX

     BT    EAX,Def_Target_DirX_D
     SbB   EBP,EBP
     LEA   EBP,[2*EBP+1]

     BT    EAX,Def_Source_DirX_D
     SbB   EBX,EBX
     LEA   EBX,[2*EBX+1]

@@01:Mov   AL,[ESI]
     Cmp   AL,AH
     JE    @@00
     Mov   [EDI],AL
@@00:Add   ESI,EBX
     Add   EDI,EBP
     Loop  @@01

     Mov   ECX,[ESP]

     Add   ESI,[ESP+4]
     Add   EDI,[ESP+8]

     Dec   EDX
     JNE   @@01

     Add   ESP,12

End;

How can it be optimized? For example, subdividing it into several sub-routines: In this case I don't flip on Y axis the image:

Procedure Sub_MoveSprite_FX0; Assembler;

{Sub-ROUTINE per MoveSprite (FlipY).

 INPUT:

    EAX= Attributi immagine.
    EBX= Scarto X per origine.
    ECX= Quantità X di Pixel per LINEA.
    EDX= Quantità Y di linee da trasf.
    EBP= Scarto X per destinazione.
    ESI= OFFSET per origine.
    EDI= OFFSET per destinaz}

Asm

     ClD

     Push  ECX

@@01:LodSB
     Cmp   AL,AH
     JE    @@00
     Mov   [EDI],AL
@@00:Inc   EDI
     Loop  @@01

     Mov   ECX,[ESP]

     Add   ESI,EBX
     Add   EDI,EBP

     Dec   EDX
     JNE   @@01

     Add   ESP,4

End;

Now there is here complete code of my optimization, without suggestions:

Procedure PutImage_FullClipp; Assembler;

{Calcola la CLIPPING REGION.

 INPUT:
 -----:
 EAX     : Byte_0 = BkCol, nuovo colore sfondo OBPVBuff (Ex simmetria verticale).
           Byte_1 = SpCol, nuovo colore sfondo SPRITE.
           Byte_2 = MaskCol, colore maschera/FIGURA.
           Byte_3 = Attributi EXTRA:
                    Bit0= 0 -> Col. predef. dello sfondo di OBPVBuff.
                          1 -> Il Col. dello sfondo di OBPVBuff è BkCol.
                    Bit1= 0 -> Col. predef. dello sfondo.
                          1 -> Il Col. dello sfondo è SpCol.
                    Bit2= 0 -> Rappresenta lo SPRITE.
                          1 -> Rappresenta lo sfondo dello SPRITE.
                    Bit3= 0 -> L' immagine non è uno SPRITE.
                          1 -> L' immagine è uno SPRITE;
                    Bit4= 0 -> L' immag. non è uno maschera.
                          1 -> L' immag. è uno maschera di colore MaskCol.
                    Bit5= 0 -> Put-IMAGE.
                          1 -> Get-IMAGE.
                    Bit6= 0 -> Nessuna simmetria orizzontale.
                          1 -> Simmetria orizzontale.
                    Bit7= 0 -> Nessuna simmetria verticale.
                          1 -> Simmetria verticale.
 EBX     = Dimensioni X e Y della finestra sul BUFFER VIDEO
           (specif. 0, la Dim. sarà consid. = a quella del BUFFER VIDEO).
 ECX     = COORDINATE X e Y dell' immagine OBP di origine.
 EDX     = COORDINATE X e Y della finestra sul BUFFER VIDEO.
 ESI     = PUNTATORE all' immagine OBP di origine.
 EDI     = PUNTATORE al BUFFER VIDEO OBP di destinazione.

 OUTPUT:
 ------:
 EAX     : Byte_0 = Colore sfondo OBPVBuff.
           Byte_1 = Colore sfondo SPRITE.
           Byte_2 = MaskCol, colore maschera/FIGURA.
           Byte_3 = Attributi EXTRA:
                    Bit0= 0 -> Il dithering inizia per 0.
                          1 -> Il dithering inizia per 1.
                    Bit1= 0 -> Col. predef. dello sfondo.
                          1 -> Il Col. dello sfondo è SpCol.
                    Bit2= 0 -> Rappresenta lo SPRITE.
                          1 -> Rappresenta lo sfondo dello SPRITE.
                    Bit3= 0 -> L' immagine non è uno SPRITE.
                          1 -> L' immagine è uno SPRITE;
                    Bit4= 0 -> L' immag. non è uno maschera.
                          1 -> L' immag. è uno maschera di colore MaskCol.
                    Bit5= 0 -> Put-IMAGE.
                          1 -> Get-IMAGE.
                    Bit6= 0 -> Incremento X destinazione = 1.
                          1 -> Incremento X destinazione (FlipX) = -1.
                    Bit7= 0 -> Incremento X origine = 1.
                          1 -> Incremento X origine (FlipX)= -1.
 EBX     = Scarto X per origine.
 ECX     = Quantità X di Pixel per LINEA.
 EDX     = Quantità Y di linee.
 EBP     = Scarto X per destinazione.
 ESI     = PUNTATORE ai dati da trasferire per l' origine.
 EDI     = PUNTATORE alla destinazione dei dati da trasferire.
 FCarry  = 1 -> ERRORE, i registri di OUTPUT
                non contengono valori attendibili.
         = 0 -> Ok, tutti i registri di OUTPUT
                contengono valori attendibili}

Asm

     Sub   ESP,60         {Definisce le variabili locali sullo STACK}

  (* Attr        {[ESP+00]: Attributi SPRITE}
     X           {[ESP+04]: COORDINATA X immagine da trasferire}
     Y           {[ESP+08]: COORDINATA Y immagine da trasferire}
     DimX        {[ESP+12]: Dimensione X immagine da trasferire}
     DimY        {[ESP+16]: Dimensione Y immagine da trasferire}
     WindX       {[ESP+20]: COORDINATA X finestra sul BUFFER VIDEO}
     WindY       {[ESP+24]: COORDINATA Y finestra sul BUFFER VIDEO}
     DimWindX    {[ESP+28]: Dimensione X finestra sul BUFFER VIDEO}
     DimWindY    {[ESP+32]: Dimensione Y finestra sul BUFFER VIDEO}
     ADimX       {[ESP+36]: Dimens. X immagine da trasf. allineata ai 16 BYTE}
     ADimScrX    {[ESP+40]: Dimensione X BUFFER VIDEO allineata ai 16 BYTE}
     SourcePtr   {[ESP+44]: PUNTATORE all' immagine OBP di origine}
     TargetPtr   {[ESP+48]: PUNTATORE alla destinazione dei dati da trasferire}
     FlipX       {[ESP+52]: Maschera x simmetria orizzontale}
     FlipY       {[ESP+56]: Maschera x simmetria verticale} *)

    {Preparazione iniziale e controllo coerenza parametri}

    {  SI<1 |   DI<1 |       CL |  FSign
     -------+--------+----------+-------
          0 |      0 | -0 -0  0 |      0
          0 |      1 | -0 -1 -1 |      1
          1 |      0 | -1 -0 -1 |      1
          1 |      1 | -1 -1 -2 |      1
     -------+--------+----------+-------}

     Cmp   ESI,1          {Se IMAGE=NIL, ...}
     SbB   EBP,EBP        {... EBP vale -1, altrim. vale 0}

     Cmp   EDI,1          {Se OBPVBuff=NIL, ...}
     SbB   EBP,0          {... EBP vale EBP-1, altrim. vale EBP}

     StC                  {Se IMAGE=NIL o se OBPVBuff=NIL, imposta FCarry=1 ...}
     JS    @@Ex           {... ed esce}

    {-----------------------------------}

     Mov   EBP,[ESI]      {Carica Dim_XY(IMAGE) IN EBP}
     Add   ESI,DimOBP_H   {ESI punta all' area dati di IMAGE}
     Mov   [ESP+44],ESI   {Salva ESI su SourcePtr}

    {-----------------------------------}

     MovSX ESI,BP         {Estende con SEGNO ...}
     Or    ESI,ESI        {... la Dim. X di IMAGE IN ESI; ...}
     StC                  {... se è <=0, imposta FCarry=1 ...}
     JLE   @@Ex           {... ed esce}

     Or    EBP,EBP        {Estende con SEGNO ...}
     SAR   EBP,16         {... la Dim. Y di IMAGE IN EBP; ...}
     StC                  {... se è <=0, imposta FCarry=1 ...}
     JLE   @@Ex           {... ed esce}

    {-----------------------------------}

     Mov   [ESP+12],ESI   {Salva la Dim. X di IMAGE con 0 esteso su DimX}
     Mov   [ESP+16],EBP   {Salva la Dim. Y di IMAGE con 0 esteso su DimY}

    {-----------------------------------}

     Mov   EBP,ESI        {Calcola, IN EBP, la Dim. X di IMAGE ...}
     And   EBP,R_OBP_Al-1 {...}
     Neg   EBP            {...}
     Add   EBP,R_OBP_Al   {...}
     And   EBP,R_OBP_Al-1 {...}
     Add   EBP,ESI        {... allineata ai 16 BYTE e ...}
     Mov   [ESP+36],EBP   {... la salva su ADimX}

    {-----------------------------------}

     Mov   ESI,[EDI]      {Carica Dim_XY(OBPVBuff) IN ESI}
     Add   EDI,DimOBP_H   {EDI punta all' area dati di OBPVBuff}
     Mov   [ESP+48],EDI   {Salva EDI su TargetPtr}

    {-----------------------------------}

     MovSX EDI,SI         {Estende con SEGNO ...}
     Or    EDI,EDI        {... la Dim. X di OBPVBuff IN EDI; ...}
     StC                  {... se è <=0, imposta FCarry=1 ...}
     JLE   @@Ex           {... ed esce}

     Or    ESI,ESI        {Estende con SEGNO ...}
     SAR   ESI,16         {... la Dim. Y di OBPVBuff IN ESI; ...}
     StC                  {... se è <=0, imposta FCarry=1 ...}
     JLE   @@Ex           {... ed esce}

    {-----------------------------------}

     Test  EAX,_Def_Attr_New_BkCol {Se il Bit0 di Attr.MODE <> 0 ...}
     JNE   @@00           {... usa il nuovo colore dello sfondo di OBPVBuff}

     Mov   EBP,[ESP+48]   {Carica TargetPtr IN EBP e ...}
     Mov   AL,[EBP]       {... carica IN AL il colore sfondo di OBPVBuff}

@@00:Test  EAX,_Def_Attr_New_SpCol {Se il Bit1 di Attr.MODE <> 0 ...}
     JNE   @@01           {... usa il nuovo colore dello sfondo di IMAGE}

     Mov   EBP,[ESP+44]   {Carica SourcePtr IN EBP e ...}
     Mov   AH,[EBP]       {... carica IN AH il colore sfondo di IMAGE}

@@01:BT    EAX,Def_Attr_FlipY_D {Bit7(Attr.MODE)<>0? Si: FCarry=1; no: FCarry=0}
     SbB   EBP,EBP        {Se Bit7 di Attr.MODE <>0, EBP=-1, altrim. EBP=0}
     Mov   [ESP+56],EBP   {Imposta FlipY=EBP}

     BT    EAX,Def_Attr_FlipX_D {Bit6(Attr.MODE)<>0? Si: FCarry=1; no: FCarry=0}
     SbB   EBP,EBP        {Se Bit6 di Attr.MODE <>0, EBP=-1, altrim. EBP=0}
     Mov   [ESP+52],EBP   {Imposta FlipX=EBP}

     And   EBP,_Def_Target_DirX {EBP contiene il Bit6 di Attr.MODE}
     And   EAX,-1-_Def_Source_DirX-_Def_Dither {CANC. Bit0 e Bit7 di Attr.MODE}

     Test  EAX,_Def_Attr_Get_Image {Se il Bit5 di Attr.MODE=0 ...}
     JE    @@02           {... salta}

     XChg  AL,AH          {Scambia i colori dello sfondo di IMAGE e OBPVBuff}
     Add   EAX,EBP        {Scambia il Bit6 Col Bit7 di Attr.MODE}

@@02:Mov   [ESP],EAX      {Salva Attr.MODE su Attr}

    {-----------------------------------}

     Mov   EAX,EDI        {Calcola, IN EAX, la Dim. X di OBPVBuff ...}
     And   EAX,R_OBP_Al-1 {...}
     Neg   EAX            {...}
     Add   EAX,R_OBP_Al   {...}
     And   EAX,R_OBP_Al-1 {...}
     Add   EAX,EDI        {... allineata ai 16 BYTE e ...}
     Mov   [ESP+40],EAX   {... la salva su ADimScrX}

    {-----------------------------------}

     MovSX EAX,CX         {Calcola, IN EAX, la coord. X di IMAGE, ...}
     Mov   [ESP+4],EAX    {... con SEGNO esteso e la salva su X}

     Or    ECX,ECX        {Calcola, IN ECX, la coord. Y di IMAGE, ...}
     SAR   ECX,16         {... con SEGNO esteso e ...}
     Mov   [ESP+8],ECX    {... la salva su Y}

    {-----------------------------------}

     MovSX EAX,DX         {Estende con SEGNO la coord. X ...}
     Or    EAX,EAX        {... della finestra sul BUFFER VIDEO IN EAX; ...}
     StC                  {... se è <0, imposta FCarry=1 ...}
     JS    @@Ex           {... ed esce}

     Or    EDX,EDX        {Estende con SEGNO la coord. Y ...}
     SAR   EDX,16         {... della finestra sul BUFFER VIDEO IN EDX; ...}
     StC                  {... se è <0, imposta FCarry=1 ...}
     JS    @@Ex           {... ed esce}

    {-----------------------------------}

     Mov   [ESP+20],EAX   {Salva la coord. X fin. BUFFER Vid. con 0 e. su WindX}
     Mov   [ESP+24],EDX   {Salva la coord. Y fin. BUFFER Vid. con 0 e. su WindY}

    {-----------------------------------}

     MovSX ECX,BX         {Estende con SEGNO la dimens. X ...}
     Or    ECX,ECX        {... della finestra sul BUFFER VIDEO IN ECX; ...}
     StC                  {... se è <0, imposta FCarry=1 ...}
     JS    @@Ex           {... ed esce}

     JNE   @@03           {Se la dimens. X della finestra sul BUFFER VIDEO ...}
     Mov   ECX,EDI        {... è =0, la imposta con la Dim. X di OBPVBuff}

@@03:Add   EAX,ECX        {Se la coord. X della finestra sul BUFFER VIDEO ...}
     Cmp   EDI,EAX        {... più la Dim. X finestra sul BUFFER VIDEO ...}
     JB    @@Ex           {... > Dim. X di OBPVBuff, esce (FCarry=1)}

    {-----------------------------------}

     Or    EBX,EBX        {Estende con SEGNO la dimens. Y ...}
     SAR   EBX,16         {... della finestra sul BUFFER VIDEO IN EBX ...}
     StC                  {... se è <0, imposta FCarry=1 ...}
     JS    @@Ex           {... ed esce}

     JNE   @@04           {Se la dimens. Y della finestra sul BUFFER VIDEO ...}
     Mov   EBX,ESI        {... è =0, la imposta con Dim. Y di OBPVBuff}

@@04:Add   EDX,EBX        {Se la coord. Y della finestra sul BUFFER VIDEO ...}
     Cmp   ESI,EDX        {... più la Dim. Y finestra sul BUFFER VIDEO ...}
     JB    @@Ex           {... > Dim. Y di OBPVBuff, esce (FCarry=1)}

    {-----------------------------------}

     Mov   [ESP+28],ECX   {Salva la Dim.X fin. BUFFER Vid. con 0 e. su DimWindX}
     Mov   [ESP+32],EBX   {Salva la Dim.Y fin. BUFFER Vid. con 0 e. su DimWindY}

    {Calcola scarti XLow (ESI) e XHigh (ECX)}

     Mov   EDI,[ESP+4]    {Carica, IN EDI, la coord. X di IMAGE}
     Mov   EBX,[ESP+12]   {Carica, IN EBX, la Dim. X di IMAGE}

     XOr   ESI,ESI        {Imposta XLow=0}
     XOr   ECX,ECX        {Imposta XHigh=0}

     Or    EDI,EDI        {Se EDI>=0 ...}
     JNS   @@05           {... salta}

     Add   EDI,EBX        {Imposta EDI=EDI+EBX; ...}
     StC                  {... se EDI<0, ...}
     JS    @@Ex           {... imposta FCarry=1 ed esce}

     Mov   ESI,[ESP+4]    {Imposta XLow con la coord. X di IMAGE}
     Neg   ESI            {Imposta XLow=-XLow}
     Mov   DWord Ptr [ESP+4],0 {Imposta la coord. X di IMAGE a 0}

     Jmp   @@06           {Salta}

@@05:Cmp   EDI,[ESP+28]   {Se EDI>=Dim. X della finestra su OBPVBuff ...}
     CmC                  {...}
     JB    @@Ex           {... imposta FCarry=1 ed esce}

     Add   EDI,EBX        {Imposta EDI=EDI+EBX}

@@06:Cmp   EDI,[ESP+28]   {Se EDI<=Dim. X della finestra su OBPVBuff ...}
     JBE   @@07           {... salta}

     Mov   ECX,EDI        {Imposta ...}
     Sub   ECX,[ESP+28]   {... XHigh=EDI-Dim. X della finestra su OBPVBuff}

    {Calcola scarti YLow (EDI) e YHigh (EBX)}

@@07:Mov   EAX,[ESP+8]    {Carica, IN EAX, la coord. Y di IMAGE}
     Mov   EDX,[ESP+16]   {Carica, IN EDX, la Dim. Y di IMAGE}

     XOr   EDI,EDI        {Imposta YLow=0}
     XOr   EBX,EBX        {Imposta YHigh=0}

     Or    EAX,EAX        {Se EAX>=0 ...}
     JNS   @@08           {... salta}

     Add   EAX,EDX        {Imposta EAX=EAX+EDX; ...}
     StC                  {... se EAX<0, ...}
     JS    @@Ex           {... imposta FCarry=1 ed esce}

     Mov   EDI,[ESP+8]    {Imposta YLow con la coord. Y di IMAGE}
     Neg   EDI            {Imposta YLow=-YLow}
     Mov   DWord Ptr [ESP+8],0 {Imposta la coord. Y di IMAGE a 0}

     Jmp   @@09           {Salta}

@@08:Cmp   EAX,[ESP+32]   {Se EAX>=Dim. Y della finestra su OBPVBuff ...}
     CmC                  {...}
     JB    @@Ex           {... imposta FCarry=1 ed esce}

     Add   EAX,EDX        {Imposta EAX=EAX+EDX}

@@09:Cmp   EAX,[ESP+32]   {Se EAX<=Dim. Y della finestra su OBPVBuff ...}
     JBE   @@10           {... salta}

     Mov   EBX,EAX        {Imposta ...}
     Sub   EBX,[ESP+32]   {... YHigh=EAX-Dim. Y della finestra su OBPVBuff}

    {Prepara i registri di OUTPUT EAX, EBX, ECX, EDX, EDI, ESI}

@@10:Cmp   DWord Ptr [ESP+56],-1 {Se FlipY=-1 ...}
     Mov   EAX,EDI        {...}
     CMovE EDI,EBX        {...}
     CMovE EBX,EAX        {... scambia YHigh con YLow}

    {-----------------------------------}

     Add   EBX,EDI        {    EBX=  YHigh+YLow}
     Neg   EBX            {    EBX= -YHigh-YLow}
     Add   EBX,[ESP+16]   {    EBX=  DimY-YHigh-YLow;         N.righe da trasf.}

     StC                  {Se non CI sono righe da trasf.,    FCarry=1 ...}
     JE    @@Ex           {... esce}

    {-----------------------------------}

     Cmp   DWord Ptr [ESP+52],-1 {Se FlipX=-1 ...}
     Mov   EAX,ESI        {...}
     CMovE ESI,ECX        {...}
     CMovE ECX,EAX        {... scambia XHigh con XLow}

    {-----------------------------------}

     Add   ECX,ESI        {    ECX=  XHigh+XLow}
     Neg   ECX            {    ECX= -XHigh-XLow}
     Add   ECX,[ESP+12]   {    ECX=  DimX-XHigh-XLow;         N.Col. da trasf.}

     StC                  {Se non CI sono colonne da trasf.,  FCarry=1 ...}
     JE    @@Ex           {... esce}

    {-----------------------------------}

     Mov   EBP,ESI        {    EBP=  XLow}
     XOr   EBP,EDI        {    EBP=  XLow ^ YLow}
     And   EBP,1          {    EBP=  (XLow ^ YLow) & 1}
     ShL   EBP,Def_Dither_D {  EBP=  ((XLow ^ YLow) & 1)<<24}
     Or    [ESP],EBP      {    Attr.MODE= Attr.MODE | ((XLow ^ YLow) & 1)}

    {-----------------------------------}

     Mov   EBP,ECX        {    EBP= -XLow-XHigh+DimX}
     Dec   EBP            {    EBP= -XLow-XHigh+DimX-1}
     And   EBP,[ESP+52]   {    EBP= (-XLow-XHigh+DimX-1) & FlipX}

     Add   EBP,[ESP+4]    {    EBP=  X+[-XLow-XHigh+DimX-1]}
     Add   EBP,[ESP+20]   {    EBP=  X+[-XLow-XHigh+DimX-1]+WindX}

     Mov   EAX,EBX        {    EAX= -YLow-YHigh+DimY}
     Dec   EAX            {    EAX= -YLow-YHigh+DimY-1}
     And   EAX,[ESP+56]   {    EAX= (-YLow-YHigh+DimY-1) & FlipY}

     Add   EAX,[ESP+8]    {    EAX=  Y+[-YLow-YHigh+DimY-1]}
     Add   EAX,[ESP+24]   {    EAX=  Y+[-YLow-YHigh+DimY-1]+WindY}

     Mul   DWord Ptr [ESP+40] {EDX:EAX= (Y+[-YLow-YHigh+DimY-1]+WindY)*ADimScrX}

     Add   EAX,EBP        {    EAX= (Y+[-YLow-YHigh+DimY-1]+WindY)*ADimScrX+ ...}
                          {...       +X+[-XLow-XHigh+DimX-1]+WindX}
     Add   [ESP+48],EAX   {TargetPtr=(Y+[-YLow-YHigh+DimY-1]+WindY)*ADimScrX+...}
                          {...        +X+[-XLow-XHigh+DimX-1]+WindX+TargetPtr}

    {-----------------------------------}

     Mov   EAX,EDI        {    EAX=  YLow}

     Mul   DWord Ptr [ESP+36] {EDX:EAX=  YLow*ADimX}

     Add   EAX,ESI        {    EAX=  YLow*ADimX+XLow}
     Add   [ESP+44],EAX   {SourcePtr=YLow*ADimX+XLow+SourcePtr}

    {-----------------------------------}

     Mov   EDX,EBX        {    EDX=  DimY-YHigh-YLow;         N.righe da trasf.}

     Mov   EBX,ECX        {    EBX=  DimX-XHigh-XLow}
     Neg   EBX            {    EBX= -DimX+XHigh+XLow}
     Mov   EAX,EBX        {    EAX= -DimX+XHigh+XLow}

     Add   EBX,[ESP+36]   {    EBX= -DimX+XHigh+XLow+ADimX;   scarto X per Orig}
     Add   EAX,[ESP+40]   {    EAX=-DimX+XHigh+XLow+ADimScrX; scarto X per Dest}

  (*   SX= Scarto X per Dest. (EAX)
     NCol= N.Col. da trasf. (ECX)
     ------+-------+---------------
     FlipX | FlipY | ScartoXDest
        0  |    0  |         SX
        0  |   -1  | -2*NCol-SX)
       -1  |    0  |  2*NCol+SX
       -1  |   -1  |        -SX *)

     LEA   EDI,[2*ECX]    {    EDI=  2*ECX}
     Mov   EBP,[ESP+52]   {    ...}
     XOr   EBP,[ESP+56]   {... EBP= FlipX ^ FlipY}
     And   EDI,EBP        {    EDI=  2*NCol & (FlipX ^ FlipY)}
     Add   EAX,EDI        {    EAX=  SX+2*NCol & (FlipX ^ FlipY)}

     XOr   EAX,[ESP+56]   {Se FlipY=-1 allora EAX=-EAX ...}
     Sub   EAX,[ESP+56]   {... altrimenti EAX è inalterato}

     Mov   EBP,EAX        {    EBP= Scarto X per Dest}

     Mov   EDI,[ESP+48]   {    EDI= PTR Dest.}
     Mov   ESI,[ESP+44]   {    ESI= PTR Orig.}
     Mov   EAX,[ESP]      {    EAX= Attr}

     Test  EAX,_Def_Attr_Get_Image {Se il Bit5 di Attr.MODE=0 ...}
     JE    @@Ex           {... salta}

     XChg  ESI,EDI        {    Scambia PTR Orig. con PTR Dest.}
     XChg  EBX,EBP        {    Scambia scarto X per Orig. con scarto X per Dest}

     ClC                  {    FCarry=0;                      nessun ERRORE}

    {-----------------------------------}

@@Ex:LEA   ESP,[ESP+60]   {    Reimposta lo STACK-POINTER;    esce}

End;

Procedure MoveImage(Image:T_Image_Ptr;
                    Prop:T_Long_Attrib_Ptr;
                    OBPVBuff:Pointer;
                    Clipp:T_Clipp_Rect_Ptr); Assembler;

{Disegna un immagine con CLIPPING REGION.

 INPUT:
 EAX   = PUNTATORE a RECORD di definizione dell' immagine.
 EDX   = PUNTATORE alle proprietà dell' immagine.
 ECX   = PUNTATORE al BUFFER VIDEO IN formato OBP.
 Clipp = PUNTATORE al RECORD della CLIPPING REGION.

 PARAMETERs order:
 1°=EAX; 2°=EDX; 3°=ECX}

Asm

     Push  EBX
     Push  EDI
     Push  ESI
     Push  EBP

     Mov   ESI,Image      {EAX}
     Mov   EBX,Prop       {EDX}
     Mov   EDI,OBPVBuff   {ECX}
     Mov   EDX,Clipp

     Mov   EAX,EBX

     Or    EBX,EBX
     JE    @@00

     Mov   EAX,[EBX]
     Mov   EBX,[EBX+4]

@@00:Push  EBX

     Or    ESI,ESI
     JE    @@01

     Mov   ECX,[ESI]
     Mov   ESI,[ESI+4]

     Mov   EBX,EDX

     Or    EDX,EDX
     JE    @@02

     Mov   EDX,[EBX]      {Clipp.XY}
     Mov   EBX,[EBX+4]    {Clipp.Dim_XY}

@@02:Call  PutImage_FullClipp
     JB    @@01

  (* _DirX | _Sprite | ShadowTable | _Mask | _BackG | FUNCTION
     ------+---------+-------------+-------+--------+---------
         0 |       0 |           0 |     0 |      0 | Sub_MoveImage_FX0
         0 |       0 |           0 |     0 |      1 | Sub_MoveImage_FX0
         0 |       0 |           0 |     1 |      0 | Sub_MoveImageMask_FX0
         0 |       0 |           0 |     1 |      1 | Sub_MoveImageMask_FX0
         0 |       0 |           1 |     0 |      0 | Sub_MoveImageShadow_FX0
         0 |       0 |           1 |     0 |      1 | Sub_MoveImageShadow_FX0
         0 |       0 |           1 |     1 |      0 | Sub_MoveImageShadow_FX0
         0 |       0 |           1 |     1 |      1 | Sub_MoveImageShadow_FX0
         0 |       1 |           0 |     0 |      0 | Sub_MoveSprite_FX0
         0 |       1 |           0 |     0 |      1 | Sub_MoveSpriteBk_FX0
         0 |       1 |           0 |     1 |      0 | Sub_MoveSpriteMask_FX0
         0 |       1 |           0 |     1 |      1 | Sub_MoveSpriteBkMsk_FX0
         0 |       1 |           1 |     0 |      0 | Sub_MoveSpriteShad_FX0
         0 |       1 |           1 |     0 |      1 | Sub_MoveSprBkShad_FX0
         0 |       1 |           1 |     1 |      0 | Sub_MoveSpriteShad_FX0
         0 |       1 |           1 |     1 |      1 | Sub_MoveSprBkShad_FX0
         1 |       0 |           0 |     0 |      0 | Sub_MoveImage
         1 |       0 |           0 |     0 |      1 | Sub_MoveImage
         1 |       0 |           0 |     1 |      0 | Sub_MoveImageMask
         1 |       0 |           0 |     1 |      1 | Sub_MoveImageMask
         1 |       0 |           1 |     0 |      0 | Sub_MoveImageShadow
         1 |       0 |           1 |     0 |      1 | Sub_MoveImageShadow
         1 |       0 |           1 |     1 |      0 | Sub_MoveImageShadow
         1 |       0 |           1 |     1 |      1 | Sub_MoveImageShadow
         1 |       1 |           0 |     0 |      0 | Sub_MoveSprite
         1 |       1 |           0 |     0 |      1 | Sub_MoveSpriteBk
         1 |       1 |           0 |     1 |      0 | Sub_MoveSpriteMask
         1 |       1 |           0 |     1 |      1 | Sub_MoveSpriteBkMask
         1 |       1 |           1 |     0 |      0 | Sub_MoveSpriteShadow
         1 |       1 |           1 |     0 |      1 | Sub_MoveSpriteBkShadow
         1 |       1 |           1 |     1 |      0 | Sub_MoveSpriteShadow
         1 |       1 |           1 |     1 |      1 | Sub_MoveSpriteBkShadow *)

     Test  EAX,_Def_DirX
     JE    @@03

     Test  EAX,_Def_Attr_Sprite
     JNE   @@04

     Cmp   DWord Ptr [ESP],0
     JNE   @@05

     Test  EAX,_Def_Attr_Mask
     JNE   @@06

     Call  Sub_MoveImage
     Jmp   @@01

@@06:Call  Sub_MoveImageMask
     Jmp   @@01

@@05:Call  Sub_MoveImageShadow
     Jmp   @@01

@@04:Cmp   DWord Ptr [ESP],0
     JNE   @@07

     Test  EAX,_Def_Attr_Mask
     JNE   @@08

     Test  EAX,_Def_Attr_BackG
     JNE   @@09

     Call  Sub_MoveSprite
     Jmp   @@01

@@09:Call  Sub_MoveSpriteBk
     Jmp   @@01

@@08:Test  EAX,_Def_Attr_BackG
     JNE   @@10

     Call  Sub_MoveSpriteMask
     Jmp   @@01

@@10:Call  Sub_MoveSpriteBkMask
     Jmp   @@01

@@07:Test  EAX,_Def_Attr_BackG
     JNE   @@11

     Call  Sub_MoveSpriteShadow
     Jmp   @@01

@@11:Call  Sub_MoveSpriteBkShadow
     Jmp   @@01

@@03:Test  EAX,_Def_Attr_Sprite
     JNE   @@12

     Cmp   DWord Ptr [ESP],0
     JNE   @@13

     Test  EAX,_Def_Attr_Mask
     JNE   @@14

     Call  Sub_MoveImage_FX0
     Jmp   @@01

@@14:Call  Sub_MoveImageMask_FX0
     Jmp   @@01

@@13:Call  Sub_MoveImageShadow_FX0
     Jmp   @@01

@@12:Cmp   DWord Ptr [ESP],0
     JNE   @@15

     Test  EAX,_Def_Attr_Mask
     JNE   @@16

     Test  EAX,_Def_Attr_BackG
     JNE   @@17

     Call  Sub_MoveSprite_FX0
     Jmp   @@01

@@17:Call  Sub_MoveSpriteBk_FX0
     Jmp   @@01

@@16:Test  EAX,_Def_Attr_BackG
     JNE   @@18

     Call  Sub_MoveSpriteMask_FX0
     Jmp   @@01

@@18:Call  Sub_MoveSpriteBkMsk_FX0
     Jmp   @@01

@@15:Test  EAX,_Def_Attr_BackG
     JNE   @@19

     Call  Sub_MoveSpriteShad_FX0
     Jmp   @@01

@@19:Call  Sub_MoveSprBkShad_FX0

@@01:Add   ESP,4

     Pop   EBP
     Pop   ESI
     Pop   EDI
     Pop   EBX

End;

Solution

  • Never use the slow loop instruction when optimizing for speed, except on AMD CPUs where it's not actually slow.

    Avoid partial-register slowdowns by using movzx for byte loads. (lodsb is slower than movzx + inc esi, so don't use it either). See http://agner.org/optimize/ for more x86 performance optimization stuff. (And also the links in the x86 tag wiki.)


    Your 2nd function is just a blend based on the bytes not being a special transparent value. Use SSE2 to optimize it with pcmpeqb and then blend with pand / pandn + por. Or SSE4.1 pblendvb.

    SSSE3 pshufb lets you more efficiently broadcast a byte to all positions.

    Especially if you make a version with a fixed width, like 16, 24, or 32 pixels wide, you'd avoid needing much in the way of scalar cleanup code to handle odd widths. But anything wider than 16 should be doable with potentially-overlapping first/last unaligned vector stores.


    And yes, making different versions of the function instead of passing integer flip / no-flip parameters is probably good, especially if you do the flipping with SSSE3 pshufb (or multiple SSE2 shuffles, or even scalar bswap). With pshufb you could have an identity shuffle (that leaves a vector unchanged instead of reversing), but it would be more efficient to have a separate loop for the no-flip case that just don't use pshufb at all.