flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > Jump sequence

Author
Thread Post new topic Reply to topic
CandyMan



Joined: 04 Sep 2009
Posts: 413
Location: film "CandyMan" directed through Bernard Rose OR Candy Shop
CandyMan 09 Sep 2021, 10:48
Code:
        jmp     .D ;0xEB, 0x07
        jmp     .C ;0xEB, 0x04
        jmp     .A ;0xEB, 0x02
        jmp     .B ;0xEB, 0x00
.C:
.B:
.A:
        nop
.D:    


Is it possible to automatically skip jumps to the next (code 0xEB, 0x00) so that only remains ?:

Code:
        jmp     .D
        nop
.D:    

_________________
smaller is better
Post 09 Sep 2021, 10:48
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8354
Location: Kraków, Poland
Tomasz Grysztar 09 Sep 2021, 11:10
My article about pitfalls of optimistic multi-pass assembly actually uses this scenario as an example to discuss the problems and possible solutions.
Post 09 Sep 2021, 11:10
View user's profile Send private message Visit poster's website Reply with quote
CandyMan



Joined: 04 Sep 2009
Posts: 413
Location: film "CandyMan" directed through Bernard Rose OR Candy Shop
CandyMan 09 Sep 2021, 16:08
I used you macro and it works.
Code:
macro jmp target
{
        local ..after,sticky
        if target <> ..after | definite target | sticky = 1
                jmp     target
                sticky = 1
        else
                sticky = 0
        end if
        ..after:
}    

Thanks.

_________________
smaller is better
Post 09 Sep 2021, 16:08
View user's profile Send private message Reply with quote
CandyMan



Joined: 04 Sep 2009
Posts: 413
Location: film "CandyMan" directed through Bernard Rose OR Candy Shop
CandyMan 16 Sep 2021, 15:53
How to automatically replace jbe with ja and omit jmp instruction? Macro is welcome.
Code:
        jbe     .1      ;\ jbe .1 -> ja .2
        jmp     .2      ;/
.1:
        nop
.2:    

_________________
smaller is better
Post 16 Sep 2021, 15:53
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 957
Location: Russia
macomics 16 Sep 2021, 16:54
macro jmp target
{
local .addr, .offs, .test
load .test word from $ - 2
if ( -16 and .test = 0x70 ) and ( $ + ( .test / 256 ) = .addr )
.test = .test xor 1
store word .test at $ - 2
else
load .test word from $ - 6
load .offs dword from $ - 4
if ( -16 and .test = 0x0F80 ) and ( $ + .offs = .addr )
.test = .test xor 1
store word .test at $ - 6
else
jmp target
end if
end if
.addr:
}
Post 16 Sep 2021, 16:54
View user's profile Send private message Reply with quote
CandyMan



Joined: 04 Sep 2009
Posts: 413
Location: film "CandyMan" directed through Bernard Rose OR Candy Shop
CandyMan 16 Sep 2021, 17:51
macomics wrote:
macro jmp target
{
local .addr, .offs, .test
load .test word from $ - 2
if ( -16 and .test = 0x70 ) and ( $ + ( .test / 256 ) = .addr )
.test = .test xor 1
store word .test at $ - 2
else
load .test word from $ - 6
load .offs dword from $ - 4
if ( -16 and .test = 0x0F80 ) and ( $ + .offs = .addr )
.test = .test xor 1
store word .test at $ - 6
else
jmp target
end if
end if
.addr:
}

Sorry but this not work.
Code:
A.asm [5]:
        jmp     .2      ;/
jmp2.asm [6] jmp [3]:
if ( -16 and .test = 0x70 ) and ( $ + ( .test / 256 ) = .addr )
processed: if(-16 and .test?2=0x70)and($+(.test?2/256)=.addr?0)
error: invalid expression.    

_________________
smaller is better
Post 16 Sep 2021, 17:51
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 957
Location: Russia
macomics 16 Sep 2021, 18:31
Sorry
Quote:
macro jmp target
{
local .addr, .offs, .test
load .test word from $ - 2
if ( 0xF0 and .test = 0x70 ) & ( $ + ( .test / 256 ) = .addr )
.test = .test xor 1
store word .test at $ - 2
else
load .test word from $ - 6
load .offs dword from $ - 4
if ( -16 and .test = 0x0F80 ) & ( $ + .offs = .addr )
.test = .test xor 1
store word .test at $ - 6
else
jmp target
end if
end if
.addr:
}


Last edited by macomics on 16 Sep 2021, 20:57; edited 3 times in total
Post 16 Sep 2021, 18:31
View user's profile Send private message Reply with quote
CandyMan



Joined: 04 Sep 2009
Posts: 413
Location: film "CandyMan" directed through Bernard Rose OR Candy Shop
CandyMan 16 Sep 2021, 20:27
Now removes the redundant jmp instruction but jumps to the wrong address. Besides, it doesn't check if the jump range is correct.

_________________
smaller is better
Post 16 Sep 2021, 20:27
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8354
Location: Kraków, Poland
Tomasz Grysztar 16 Sep 2021, 20:46
CandyMan wrote:
How to automatically replace jbe with ja and omit jmp instruction? Macro is welcome.
Code:
        jbe     .1      ;\ jbe .1 -> ja .2
        jmp     .2      ;/
.1:
        nop
.2:    
I would try something like:
Code:
macro jbe target
{
        local ..next
        define __j ..next
        if ~ defined ..next#.switch
                jbe target
        else
                ja ..next#.switch
        end if
    ..next:
}

macro jmp target
{
        if defined __j & __j relativeto $ & $ - __j = 0
                match _,__j \{ label _\#.switch at target \}
        else
                jmp target
        end if
}    
Post 16 Sep 2021, 20:46
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 957
Location: Russia
macomics 16 Sep 2021, 20:53
Tomasz Grysztar wrote:

macro jbe target
{
local ..next
define __j ..next
if ~ defined ..next#.switch
jbe target
else
ja ..next#.switch
end if
..next:
}
Then you will need 16 such pieces. For each conditional transition. But I agree that trying to change the previous instruction is a bad idea. it will turn out even more bulky
Post 16 Sep 2021, 20:53
View user's profile Send private message Reply with quote
CandyMan



Joined: 04 Sep 2009
Posts: 413
Location: film "CandyMan" directed through Bernard Rose OR Candy Shop
CandyMan 16 Sep 2021, 22:04
If I add the following macro, it doesn't work anymore.
I wish it would replace the first jump instruction with the opposite one and delete the next one for each condition.
Code:
macro ja target
{
        local ..next
        define __j ..next
        if ~ defined ..next#.switch
                ja target
        else
                jbe ..next#.switch
        end if
    ..next:
}    

_________________
smaller is better
Post 16 Sep 2021, 22:04
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 957
Location: Russia
macomics 16 Sep 2021, 22:12
Code:
        format binary as "COM"
        use32

macro jmp aim { local .a, .b, .c
  load .c word from $ - 2
  if ( 0xF0 and .c = 0x70 ) & ( $ + ( .c / 256 ) = .a )
    .b = aim - $
    if ( .b < -127 ) | ( .b > 127 )
      store word (( 0x0F and .c xor 1 ) shl 8 or 0x800F ) at $ - 2
                dd .b - 4
    else
      store word (( 0xFF and .c xor 1 ) or ( .b shl 8 )) at $ - 2
    end if
  else
    load .c word from $ - 6
    load .b dword from $ - 4
    if ( 0xF0FF and .c = 0x800F ) & ( $ + .b = .a )
      store word ( .c xor 0x0100 ) at $ - 6
      store dword ( aim - $ ) at $ - 4
    else
      jmp aim
    end if
  end if
 .a: }

rept RC n {ja b#n
        jmp @f
b#n:    nop
@@: }    


And yet I have completed my own version of the macro. But it will obviously take more memory from the fasm than the Tomasz Grysztar version. This is already an option typed not in the browser, but checked. It generates everything correctly, but it does not fit more than 13 uses per 64 kb. Another option allows you to generate 17 uses per 64 kb.
add: Slightly reduced the required amount of memory
Code:
fasm ./temp.asm ./temp -m 64 -d RC=18
flat assembler  version 1.73.28  (64 kilobytes memory)
3 passes, 54 bytes.

fasm ./temp.asm ./temp -m 64 -d RC=19
flat assembler  version 1.73.28  (64 kilobytes memory)
error: out of memory.
    

I immediately clarify. There is no length analysis logic in the macro. I hope that in 32/64 bit modes, the compiler will not start generating 16-bit conditional long jumps.


Last edited by macomics on 17 Sep 2021, 09:33; edited 3 times in total
Post 16 Sep 2021, 22:12
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8354
Location: Kraków, Poland
Tomasz Grysztar 17 Sep 2021, 07:12
CandyMan wrote:
If I add the following macro, it doesn't work anymore.
I wish it would replace the first jump instruction with the opposite one and delete the next one for each condition.
Code:
macro ja target
{
        local ..next
        define __j ..next
        if ~ defined ..next#.switch
                ja target
        else
                jbe ..next#.switch
        end if
    ..next:
}    
You could change the case to avoid macros calling each other:
Code:
macro ja target
{
        local ..next
        define __j ..next
        if ~ defined ..next#.switch
                JA target
        else
                JBE ..next#.switch
        end if
    ..next:
}    
or use EQU to refer to built-in instructions without a possibility of inadvertently calling a macro:
Code:
_ja equ ja
_jbe equ jbe

macro ja target
{
        local ..next
        define __j ..next
        if ~ defined ..next#.switch
                _ja target
        else
                _jbe ..next#.switch
        end if
    ..next:
}

macro jbe target
{
        local ..next
        define __j ..next
        if ~ defined ..next#.switch
                _jbe target
        else
                _ja ..next#.switch
        end if
    ..next:
}    
And you can keep it compact by defining all the wrappers through a single loop:
Code:
irp pair, ja:jbe, jbe:ja, jna:ja ;, ...
{
  match j:n, pair
  \{

    define j\#.p j
    define j\#.n n

    macro j target
    \\{
        local ..next
        define __j ..next
        if ~ defined ..next\\#.switch
                j\#.p target
        else
                j\#.n ..next\\#.switch
        end if
    ..next:
    \\}

  \}
}

macro jmp target
{
        if defined __j & __j relativeto $ & $ - __j = 0
                match _,__j \{ label _\#.switch at target \}
        else
                jmp target
        end if
}

define __j ..j    


Also, please keep in mind that these kinds of macros may be useful in specific context, but have several potential problems that make them unsuitable for general use. I have been mostly just demonstrating a few tricks you could use (and macomics provided a couple others), but I would not recommend using them if you just copy-paste without considering and understanding what they actually do and how it applies to your case.

One issue is that this "jmp" macro does not handle arguments other than plain target label. That's not a big deal, you could easily fix it with help of MATCH, filtering out the kinds of arguments that do not interest you.

Another one is that when you put a label before your "jmp" instruction and use this label as a jump target, but then optimize the "jmp" away, jumping to the label will no longer have the desired effect. This is something that is very hard to solve using fasm 1 (it would be easier using fasmg, which allows to intercept labels, or even all consecutive lines).
Post 17 Sep 2021, 07:12
View user's profile Send private message Visit poster's website Reply with quote
CandyMan



Joined: 04 Sep 2009
Posts: 413
Location: film "CandyMan" directed through Bernard Rose OR Candy Shop
CandyMan 17 Sep 2021, 11:06
Thank you for your comprehensive answer. I got what I wanted.
Post 17 Sep 2021, 11:06
View user's profile Send private message Reply with quote
CandyMan



Joined: 04 Sep 2009
Posts: 413
Location: film "CandyMan" directed through Bernard Rose OR Candy Shop
CandyMan 17 Sep 2021, 13:25
Unfortunately, fasm cannot assemble the code below.
Code:
irp pair, \
  jo:jno, jno:jo, jb:jnb, jnb:jb, jc:jnc, jnc:jc, jnae:jae, jae:jnae, je:jne, jne:je, jz:jnz, jnz:jz,\
  ja:jna, jna:ja, jbe:jnbe, jnbe:jbe, js:jns, jns:js, jp:jnp, jnp:jp, jpe:jpo, jpo:jpe,\
  jl:jnl, jnl:jl, jge:jnge, jnge:jge, jle:jnle, jnle:jle, jg:jng, jng:jg
{
  match j:n, pair
  \{

    define j\#.p j
    define j\#.n n

    macro j target
    \\{
        \\local ..next
        define __j ..next
        if ~ defined ..next\\#.switch
                j\#.p target
        else
                j\#.n ..next\\#.switch
        end if
    ..next:
    \\}

  \}
}

macro jmp target
{
        if defined __j & __j relativeto $ & $ - __j = 0
                match _,__j \{ label _\#.switch at target \}
        else
                jmp target
        end if
}

define __j ..j

        cmp     eax,2
        jbe     .15
        jmp     .16
.15:
        jmp     .14
.14:
        jmp     .16
.16:
.19:
        jz      .15
        sub     eax,14    

Code:
A.ASM [43]:
        jmp     .14
A.ASM [31] jmp [2]:
                match _,__j \{ label _\#.switch at target \}
A.ASM [31] match [0]:
                match _,__j \{ label _\#.switch at target \}
processed: label ..next?0.switch at .14
error: symbol already defined.    

_________________
smaller is better
Post 17 Sep 2021, 13:25
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8354
Location: Kraków, Poland
Tomasz Grysztar 17 Sep 2021, 13:31
This should fix that specific issue:
Code:
macro jmp target
{
        match _,__j
        \{
                if defined _ & _ relativeto $ & $ - _ = 0 & ~ definite _\#.switch
                        label _\#.switch at target
                else
                        jmp target
                end if
        \}
}    
I see you already corrected the missing "\\" before "local".
Post 17 Sep 2021, 13:31
View user's profile Send private message Visit poster's website Reply with quote
CandyMan



Joined: 04 Sep 2009
Posts: 413
Location: film "CandyMan" directed through Bernard Rose OR Candy Shop
CandyMan 17 Sep 2021, 16:29
An additional condition is needed to check that the first jcc instruction jumps right after the second jmp instruction, otherwise everything crashes.
Code:
        or      al,al
        jnz     .625    ;\ changed to JZ .624 (this possible to modify)
        jmp     .624    ;/
.625:    
Code:
        or      al,al
        jnz     .625    ;\ changed to JZ .624 (this is bug, not possible to modify)
        jmp     .624    ;/
        lea     eax,[ebp-12]
.624:
        inc     dword [eax]
.625:
        jmp     $    

_________________
smaller is better
Post 17 Sep 2021, 16:29
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 957
Location: Russia
macomics 17 Sep 2021, 17:20
CandyMan wrote:
smaller is better

Code:
macro jmp aim { local .a, .b, .c, .d, .e
  load .b word from $ - 2
  load .c dword from $ - 4
  load .d word from $ - 6
  if ( ~ aim eqtype rax ) | (( 0xF0 and .d = 0x70 ) & ( $ + ( .b / 256 ) = .a )) | (( 0xF0FF and .d = 0x800F ) & ( $ + .c = .a ))
    display "Please note this place in the code and make it a little smaller.", 10
    err
  else
      jmp aim
  end if
 .a: }    

So you will definitely be able to get rid of all such moments. But the solution is somewhat time-consuming.

just as a solution, it's easier to create a macro that generates both of these instructions as needed than to write something that will remove unnecessary jmp

https://flatassembler.net/docs.php?article=manual wrote:
Inside macroinstruction also special operator # can be used. This operator causes two names to be concatenated into one name. It can be useful, because it's done after the arguments and local names are replaced with their values. The following macroinstruction will generate the conditional jump according to the cond argument:

macro jif op1,cond,op2,label
{
cmp op1,op2
j#cond label
}

For example jif ax,ae,10h,exit will be assembled as cmp ax,10h and jae exit instructions.


Code:
define not.ja jna
define not.jb jnb
define not.jc jnc
...
define not.jna ja
...
; or irps pair, ja:jna ...
; {
;   match cif:nif,pair \{
;     define not.\#cif nif
;     define not.\#nif cif
;    \}
;  }
  macro jif cond*, label_true*, label_false*, mispredict
{
  local .end_label, .jmp_label
  if (( label_true > label_false ) & \
      ( .end_label = label_false )) | \
     (( label_false > label_true ) & \
      ( .end_label <> label_true )) | \
      ( ~ mispredict eq )
    j#cond label_true
    .jmp_label = label_false
  else if label_true <> label_false
    not.j#cond label_false
    .jmp_label = label_true
  else label_true <> .end_label
    jmp label_true
    .jmp_label = .end_label
  end if
  if .jmp_label <> .end_label
    jmp .jmp_label
  end if
  .end_label:
}
    
something like that
Post 17 Sep 2021, 17:20
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.