1

I tried running this code in asm:

.code
main PROC
    N=8
    mov eax, 0
    mov eax, N
    call writeDec
    call crlf
    mov ecx, 2
loop1:
    N=N-2
    mov eax,N
    call writeDec
    call crlf
loop loop1
    mov eax, N
    call writedec

I noticed that the global variable N decreases by 2 only once, since the output will be

8
6
6
6 //prints after loop ends

What I wanted to ask is, why does the line N=N-2 execute only once? I get the same result by jumping to loop1 instead of looping it.

Itamar Ivri
  • 117
  • 6
  • 6
    `N` is not a global variable and `N=N-2` is a compile time construct. To make a global variable put something like `N dd 8` into a `.data` section and instead of `N=N-2` actually use an instruction such as `sub N, 2`. – Jester Jun 21 '21 at 00:02
  • @Jester Thanks, I was taught that defining a variable this way makes it global, guess they were wrong. Anyway I'm just studying for an exam so I needed to understand how things work, this is the only purpose of this code – Itamar Ivri Jun 21 '21 at 13:22

2 Answers2

4

This code is very hard to understand. N is surely not a global variable. It is rather a compile time "variable".

The situation you are witnessing is the discrepancy between a global variable and a compile time symbolic value - either defined with EQU or = (Quoting from the MASM 6.1 Programmers.Guide.PDF).

You can define symbolic integer constants with either of the data assignment directives, EQU or the equal sign (=). These directives assign values to symbols during assembly, not during program execution. Symbolic constants are used to assign names to constant values. [...]

and

The directives EQU and = have slightly different purposes. Integers defined with the = directive can be redefined with another value in your source code, but those defined with EQU cannot. Once you’ve defined a symbolic constant with the EQU directive, attempting to redefine it generates an error.

So what happens in you code is, that the initial value N=8 - which is output in your first call writeDec - is changed by the following N=N-2 to N=6.

Thing is that these symbolic integer constants (defined by =) are changed linearly with the program definition: so N will change its value to 6 after its re-definition, linearly! The value is (re)defined in relation to the linear program definition; and not the program flow, which will be determined at run-time.

So you have a loop in your program flow, but a sequence in your program definition! (Your source code is a linear text file.) So once N was changed the first time by N=N-2 in the linear program definition at compile time, it won't be changed again, because this change would have to happen at run-time.

halfer
  • 19,824
  • 17
  • 99
  • 186
zx485
  • 28,498
  • 28
  • 50
  • 59
  • 2
    You could say "The statement N=N-2 is processed once at assemble time, and executes 0 times at run-time." But yes, assemble order vs. program flow is a good point to make. – Peter Cordes Jun 21 '21 at 01:14
0

His answer would work perfectly fine, I'm just wondering why you didn't use ecx since you set it to a value of 2 already. I.E.

sub eax, ecx

If you going to correct my post, at least read enough to realize we determined the link was to the incorrect information. Thanks for giving me the option to remove it though. Carry on.

BrainOverflow
  • 34
  • 1
  • 1
  • 7
  • 1
    Yes, that would be safe for this particular code, because those Irvine32 functions don't modify EAX. (They use a calling convention that's different from normal functions; normally you'd want to keep your counter in a different register, like EBX or EDI, because those are call-preserved even in the normal conventions.) And yes, this is definitely a case where a register is the more sensible choice. – Peter Cordes Jun 21 '21 at 01:12
  • That's why I linked the page, it went into the other variables such as EBX in much more detail than I am familiar with in raw ASM such as a driver type definition personally. I actually intended to read it further myself later honestly. I'm used to doing what I believe he was trying to do like using C/C++ inline Assembler which would allow the variable to be used properly. I'm familiar enough to know your answer was a proper one by making the variable known. I was just adding my two cents. I actually tried to upvote your answer but couldn't lol. And was evidently wrong, thank you Peter – BrainOverflow Jun 21 '21 at 01:19
  • 1
    My answer that you linked doesn't talk about calling conventions at all. It just happens to use EBX as one of the *registers* (not variables) that exists on x86. For calling conventions, see [What are callee and caller saved registers?](https://stackoverflow.com/a/56178078). – Peter Cordes Jun 21 '21 at 01:21
  • 4
    `sub eax, 2` is what would produce the desired result. The `loop` instruction implicitly uses ECX (like `dec ecx/jnz` but without modifying FLAGS), so this program actually *does* use ECX. More sensible would be `mov eax,8` / looptop: `call WriteDec` and crlf / `sub eax, 2` / `jnz looptop`. (The question peeled the first print out of the loop for no apparent reason, instead of just putting the decrement after, or starting with N=10. Hopefully they only over-complicated it this way after finding that N=N-2 wasn't working the way they expected.) – Peter Cordes Jun 21 '21 at 01:23
  • That is also assuming he was using a static number of 2 at all times though...which is unlikely if your working in assembler in the first place. – BrainOverflow Jun 21 '21 at 01:25
  • @PeterCordes - I have to admit you REALLY know your stuff. I can't even find a book this thorough these days. I'm gonna have to look at some of your posts. Thanks for correcting the link by the way. I didn't know I could tag yet. – BrainOverflow Jun 21 '21 at 01:35
  • 3
    `N=N-2` clearly intended to use a constant `2`. In asm, there's no compiler; if there are any constants, you should use them, rather than making your code slower. (Of course, not hard-coding is good. You'd use assemble-time constants like `foo equ 2`, so `sub eax, foo` / `jnc looptop`, which still works even if foo isn't an exact multiple of your start value so you never hit exactly zero. You just leave the loop when your down-counter wraps. Or in real life you'd just write in C and let a C compiler do this for you. hand-writing loops like this mostly makes sense as a learning exercise.) – Peter Cordes Jun 21 '21 at 01:41
  • 1
    And re: @tagging people: you're allowed to comment under your own posts, even without having any rep yet. When you can comment at at all, you can @ notify anyone who's participated in that comment thread. – Peter Cordes Jun 21 '21 at 01:46
  • 1
    I'm learning...slower than the assembler lol. But I'll get there. Thanks for both lessons, much appreciated. @PeterCordes – BrainOverflow Jun 21 '21 at 06:42
  • 1
    @BrainOverflow Thanks. and I know using a register would be more efficient, I'm just studying for an exam and need to know how to deal with different situations such as this one – Itamar Ivri Jun 21 '21 at 13:16
  • 1
    I'm just studying to study, so i completely understand. I'm quite surprised they have you doing assembler these days. Most people tell me I need to see a shrink haha. Good luck with your exam. @ItamariIvri – BrainOverflow Jun 21 '21 at 15:16
  • 1
    @BrainOverflow Yeah hopefully we're not going through this for nothing. And thank you! – Itamar Ivri Jun 21 '21 at 15:31