Author Topic: Loop Level Control  (Read 778 times)

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Loop Level Control
« on: February 05, 2017, 06:15:31 AM »
Some more directives added in addition to .BREAK and .CONTINUE.

.BREAK[(n)] [[.IF condition]]

Generates code to terminate a .WHILE or .REPEAT block if condition is true. (n) is optional nesting level to terminate.

Code: [Select]
.while 1
    .break ; break .while 1
    .while 2
.break(1) ; break .while 1
.while 3
    .break(2) ; break .while 1
    .while 4
.break(3) ; break .while 1
.break(2) ; break .while 2
.break(1) ; break .while 3
    .endw
.endw
    .endw
.endw

.CONTINUE [[.IF condition]]
.CONTINUE[(n)] [[.IF condition]]


Generates code to jump to the top of a .WHILE or .REPEAT block if condition is true.

(n) is optional nesting level to continue.

Code: [Select]
.while 1
    .continue ; continue .while 1
    .while 2
.continue(1) ; continue .while 1
.while 3
    .continue(2) ; continue .while 1
    .while 4
.continue(3) ; continue .while 1
.continue(2) ; continue .while 2
.continue(1) ; continue .while 3
    .endw
.endw
    .endw
.endw

.CONTINUE(0) jump's directly to START label: no TEST.

Code: [Select]
.while 1
    .continue(0) ; Jump to START label
    .continue ; Jump to START label
.endw
.while eax
    .continue(0) ; Jump to START label
    .continue ; Jump to TEST label
.endw
.repeat
    .continue(0) ; Jump to START label
    .continue ; Jump to EXIT label
    .break ; Jump to EXIT label
.until 1
« Last Edit: September 20, 2017, 11:54:39 PM by nidud »

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Loop Level Control
« Reply #1 on: February 05, 2017, 07:29:29 AM »
EDIT: obsolete
« Last Edit: September 21, 2017, 12:03:49 AM by nidud »

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
.GOTOSW
« Reply #2 on: February 08, 2017, 03:31:50 AM »
Added .GOTOSW

.GOTOSW[1|2|3] [[(<case_val>)] | [.IF condition]]

Generates code to jump to the top of a .SWITCH block if condition is true.

.GOTOSW jump's to the TEST label.
Code: [Select]
.switch al
  .case 1
  ...
  .case 9
    mov al,1
    .gotosw ; "Jump" to case 1

.GOTOSW[1|2|3] is optional nesting level to continue.
Code: [Select]
.switch al
  .case 1
    .gotosw ; continue .switch al
    .switch bl
      .case 1
.gotosw1 ; continue .switch al
.switch cl
  .case 1
    .gotosw2 ; continue .switch al
    .switch dl
      .case 1
.gotosw3 ; continue .switch al
.gotosw2 ; continue .switch bl
.gotosw1 ; continue .switch cl
.gotosw1(1) ; Jump to .switch cl / case 1
    .endsw
.endsw
    .endsw
.endsw

GOTOSW can be used in combination with .IF condition, or a jump to .GOTOSW(<case_val>). If case_val exist a direct jump is created, else case_val is moved to switch_reg and a jump to the test label is created.

Since no labels below current position are created a direct jump to the default section is not possible. However, a jump to a non-existing label will in this case end up at the default label if that exist. Otherwise this will just exit the switch.

Code: [Select]
A equ 10
B equ 100

.switch al
  .case A
.gotosw(B) ; jump to B
.gotosw(B+1) ; jump to .default
  .case B
.gotosw(A) ; direct jump to A
  .default
.endsw

Code: [Select]
= A                            A equ 10
= 64                           B equ 100

00000000                        .switch al
00000000                          .case A
00000000  EB0C              *   jmp @C0001
00000002                    *   @C0002:
00000002                        .gotosw(B) ; jump to B
00000002  B064              *   mov al,B
00000004  EB08              *   jmp @C0001
00000006                        .gotosw(B+1) ; jump to .default
00000006  B065              *   mov al,B+1
00000008  EB04              *   jmp @C0001
0000000A                          .case B
0000000A                    *   @C0004:
0000000A                        .gotosw(A) ; direct jump to A
0000000A  EBF6              *   jmp @C0002
0000000C                          .default
0000000C                    *   @C0005:
0000000C                        .endsw
0000000C  EB0A              *   jmp @C0003
0000000E                    *   @C0001:
0000000E  3C0A              *   cmp al,A
00000010  74F0              *   je @C0002
00000012  3C64              *   cmp al,B
00000014  74F4              *   je @C0004
00000016  EBF4              *   jmp @C0005
00000018                    *   @C0003:

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
.FOR
« Reply #3 on: February 09, 2017, 10:07:14 AM »
Added .FOR, .FORS, and .ENDF

.FOR [initialization] : [condition] : [increment/decrement]
   statements
   .ENDF


Generates code that executes the block of statements while condition remains true. FORS is the "signed compare" version.

The inner logic of the loop is equal to .WHILE, so the .CONTINUE and .BREAK directives may be used in the same way.

Code: [Select]
.FOR::
.FOR::
  .FOR::
   .FOR::
.CONTINUE(3)
.CONTINUE(0)
.BREAK(3)
   .ENDF
  .ENDF
.ENDF
.ENDF

Argument may be added in brackets or without.

Code: [Select]
.FOR::
.ENDF
.FOR(::)
.ENDF
.FOR(cl=2::)
.ENDF
.FOR(:cl:)
.ENDF
.FOR(::cl--)
.ENDF
.FOR(cl=2:cl:)
.ENDF
.FOR(cl=2:cl:cl--)
.ENDF
.FOR(cl=2,bl=al:cl<0:--cl,bl+=4)
.ENDF

Expansion of .FOR cl = 2 : cl : cl--
Code: [Select]
00000000  B102              *   mov cl,2
00000002                    *   @C0001:
00000002  84C9              *   test cl, cl
00000004  7404              *   jz  @C0003
00000006                        .ENDF
00000006                    *   @C0002:
00000006  FEC9              *   dec cl
00000008  EBF8              *   jmp @C0001
0000000A                    *   @C0003:

initialization

Function calls may be used to assign value. Each section, separated by a comma, expands function calls individually so they may be nested.

Code: [Select]
.FOR edx = foo(1,2),
ecx = foo(1,3),
ebx = foo(1,4) ::
.ENDF

Code: [Select]
00000004  6A02     * push 2
00000006  6A01     * push 1
00000008  FF1500000000     * call foo
0000000E  8BD0     * mov edx , eax
00000010  6A03     * push 3
00000012  6A01     * push 1
00000014  FF1500000000     * call foo
0000001A  8BC8     * mov ecx , eax
0000001C  6A04     * push 4
0000001E  6A01     * push 1
00000020  FF1500000000     * call foo
00000026  8BD8     * mov ebx , eax
00000028     * @C0001:
00000028 .ENDF

condition

The same as above, but note the difference between using a comma as oppose to && or || in combination with function calls.

Code: [Select]
.FORS : edx > foo(1,2),
  ecx > foo(1,3),
  ebx > foo(1,4) :
.ENDF

Code: [Select]
00000004     * @C0001:
00000004  6A02     * push 2
00000006  6A01     * push 1
00000008  FF1500000000     * call foo
0000000E  3BD0     * cmp edx , eax
00000010  7E1E     * jng @C0003
00000012  6A03     * push 3
00000014  6A01     * push 1
00000016  FF1500000000     * call foo
0000001C  3BC8     * cmp ecx , eax
0000001E  7E10     * jng @C0003
00000020  6A04     * push 4
00000022  6A01     * push 1
00000024  FF1500000000     * call foo
0000002A  3BD8     * cmp ebx , eax
0000002C  7E02     * jng @C0003
0000002E .ENDF
« Last Edit: September 21, 2017, 12:08:00 AM by nidud »

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Loop Level Control
« Reply #4 on: February 11, 2017, 09:24:15 AM »
One of the stated goals from 2012 was to add assignment of value prior to the test:

Code: [Select]
.if ((ecx = strlen(string)) > edi)

One example used was a .FOR loop with two assignments:

Code: [Select]
.data
s1 db 'abcdefgh',0
s2 db 'abcd',0
.code
start:
.for (ebx = offset s1, edx = func(strlen,ebx) ¦ edx >= sizeof(s2) ¦ edx--)
    mov byte ptr [ebx+edx-1],0
.endfor
invoke printf,cstr(<"s1: %s",10,"s2: %s",10>),ebx,addr s2
output:
s1: abcdefgh
s2: abcd
...
mov ebx,offset s1
.for (edx = func(strlen,ebx) ¦ edx >= sizeof(s2) ¦ edx--)
output:
s1: abcd
s2: abcd

New version:
Code: [Select]
.data
s1 db 'abcdefgh',0
s2 db 'abcd',0
.code

.for (rbx = offset s1, rdi = strlen(rbx) : strlen(rbx) >= sizeof(s2) : rdi--)

mov byte ptr [rbx+rdi-1],0
.endf

printf("s1: %s\ns2: %s\n", rbx, addr s2)

Expand as follows:
Code: [Select]
00000004  48BB00000000000000*   mov rbx , offset s1
0000000E  488BCB            *    mov rcx, rbx
00000011  E800000000        *    call strlen
00000016  488BF8            *   mov rdi , rax
00000019                    *   @C0001:
00000019  488BCB            *    mov rcx, rbx
0000001C  E800000000        *    call strlen
00000021  4883F805          *   cmp rax , sizeof(s2)
00000025  720A              *   jb  @C0003
00000027  C6441FFF00            mov byte ptr [rbx+rdi-1],0
0000002C                        .endf
0000002C                    *   @C0002:
0000002C  48FFCF            *   dec rdi
0000002F  EBE8              *   jmp @C0001
00000031                    *   @C0003:

Output:
Code: [Select]
s1: abcd
s2: abcd

This expansion is currently not segment-safe (as .CASE x:x:) so a colon may not be used (ds:[di].y =) but the logic of assignment should at best be equal for all HLL directives using an expression as argument.

C-assignment is controlled by brackets ( (a = b) > c). A similar approach may be added but in this case the  assignment is always done after the dot-hll directive:
Code: [Select]
.if foo(1) ; test eax,eax
.if (ebx = foo(1)) ; test ebx,ebx

A simple test to see if an equal sign is in front of the proc will enable a fix in this case but not as a general rule. I don't like exceptions when it comes to this but now one has already been made in the FOR loop.

Well, extending this feature to all directives may indeed be useful and practical, but then again it's not really needed.

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Loop Level Control
« Reply #5 on: March 02, 2017, 06:36:38 AM »
Some changes made to the .FOR loop.

.FOR [initialization] : [condition] : [increment/decrement]
   statements
   .ENDF


The initialization and increment/decrement fields are now handled equally. This means that the three sections may include function calls, and the two above may both use ++/--/+=/-=/=. Note that it's possible to pre-pend ++/-- in these fields but this will not change the logic of the loop, so <--eax> is equal to <eax-->.

The restriction on the signed compare version (.IFS, .WHILES, .FORS, ...) is now removed. This was limited to registers returned from a function call, but now the condition is assumed signed on all compare. This is normally the default state in most HLL languages so a global option to switch between signed/unsigned may be somewhat useful in this case.

Some examples:

end of list
Code: [Select]
.for ecx = table: [ecx].t_next: ecx = [ecx].t_next
.endf
start of string
Code: [Select]
.for edx = buffer, cl = [edx] : cl == ' ' || cl == 9 : edx++, cl = [edx]
.endf
function calls
Code: [Select]
.for foo(1) += 3: bar(2) < 3 && edi: edx += foo(3)
.endf
00000004                    *    mov rcx, 1
0000000B                    *    call foo
00000010                    *   add rax ,3
00000014                    *   @C0001:
00000014                    *    mov rcx, 2
0000001B                    *    call bar
00000020                    *   cmp rax , 3
00000024                    *   jnb @C0003
00000026                    *   test edi, edi
00000028                    *   jz  @C0003
0000002A                    *   @C0002:
0000002A                    *    mov rcx, 3
00000031                    *    call foo
00000036                    *   add rdx ,rax
00000039                    *   jmp @C0001
0000003B                    *   @C0003:

Naming convention for the numeric directives:

.continue [(0..n)] [.if <expression>]
.break [(0..n)] [.if <expression>]

.continue(1)   - one level up
.continue(2)   - ...

.break(1)    - one level up
.break(2)    - ...

The .continue(0) directive is a special case as it skips the conditional test and just jumps to the start label, so a more descriptive name should be added for this.

.continue(0)
.continue(01) - one level up
.continue(02) - ...

The flag-extension for .BREAK, .ENDC, and .CONTINUE is covered by .IFxx (as in .BREAK .IF..) so this is also removed.

New test case:
Code: [Select]
.while 1
    .break ; break .while 1
    .while 2
.break(1) ; break .while 1
.while 3
    .break(2) ; break .while 1
    .while 4
.break(3) ; break .while 1
.break(2) ; break .while 2
.break(1) ; break .while 3
    .endw
.endw
    .endw
.endw

.while 1
    .continue ; continue .while 1
    .while 2
.continue(1) ; continue .while 1
.while 3
    .continue(2) ; continue .while 1
    .while 4
.continue(3) ; continue .while 1
.continue(2) ; continue .while 2
.continue(1) ; continue .while 3
    .endw
.endw
    .endw
.endw
;
; Skip TEST: .continue(0[1|2|3]) [.IF <condition>]
;
.while al
    .continue(0) ; Jump to START .while al
    .while bl
.continue(01) ; Jump to START .while al
.while cl
    .continue(02) ; Jump to START .while al
    .while dl
.continue(03) ; Jump to START .while al
.continue(02) ; Jump to START .while bl
.continue(01) ; Jump to START .while cl
.continue ; Jump to TEST .while dl
    .endw
.endw
    .endw
.endw
« Last Edit: September 21, 2017, 12:14:55 AM by nidud »

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Loop Level Control
« Reply #6 on: March 11, 2017, 05:41:22 AM »
Some changes to the .for loops initialization and increment/decrement fields.

mov reg,0 is converted to xor reg,reg.

=& added: reg = &[edi+2] --> lea reg,[edi+2]
=~ added: a =~ b --> mov a,b : not a