2

My professor gave me an assignment to convert this C code into Assembly code

int k = 0, S = 0;
for (k=0; k<100; k++)
{
if (k%2 == 0)
S += k;
else
S -= k;
}

Assembly is only a small part of my course, so we haven't gotten into a lot of the technical parts. The only issue I have is with the modulus part, we've only taken division and never had learned how to use modulus. This is as far as I've got into my work

MOV CX, 0; counter
MOV AX, 0; This represents S, we haven't learned how to declare variables in assembly, so we use registers instead)
Loop1: 
      CMP [Haven't done the modulus condition]
      JE iftrue
      JNE ifwrong
iftrue:
      ADD AX, CX
      INC CX
      CMP CX, 99
      JL Loop1
ifwrong:
      SUB AX, CX
      INC CX
      CMP CX, 99
      JL Loop1

Can you help me fill in the first condition? How do you use modulus in a comparison and check if the remainder is 0 or not?

P.S.: I've also not learned how to do conditions within loops, so the iftrue/ifwrong part is just a quick improvisation on my end, I don't know if it's efficient or not. Could that part be done in a better form?

Axel1212
  • 143
  • 2
  • 10
  • The core of the issue is that `%2` is just an inefficient way to say `&1`. =) – Matteo Italia May 13 '17 at 14:59
  • 1
    You should really look at an instruction reference. You might discover that the remainder is stored in a register; the remainder is the modulus. – David Hoelzer May 14 '17 at 02:37
  • Thanks, but like I said, Assembly is only a small portion of my course. We've barely been taught about 3-4 registers, only. Directly using the register with the remainder isn't something that my professor has taught in my class. If I use it, he would probably mark it down as wrong. Thankfully, since this is about %2, I just need to find if it's even or odd numbers. Mike's answer helped me figure out an acceptable approach to this problem. – Axel1212 May 14 '17 at 08:23
  • I suppose your professor wouldn't accept `mov ax, -50`+`ret` either, then, huh? :-) – Cody Gray - on strike May 14 '17 at 13:24
  • About `JE iftrue` + `JNE ifwrong` pair: imagine you removed the `JE iftrue` line, what will happen for ZF=1 ... the code will fall-through the `JNE` to the `iftrue:` labelled instruction `ADD AX,CX`, so you would get the same result, as in your original code, but you would save one conditional jump instruction, which is good for performance. It's about picking one of the if-else cases as "default", and jump away from it only in non-default case. Think about it... (or even better, write it your way, check it in debugger by stepping over instructions, how it works, then comment out `JE`+debug). – Ped7g May 15 '17 at 16:07

2 Answers2

5

Professors generally do not give assignments that involve things that you have not learned yet. For an external observer, (such as anyone reading this question,) most chances are that you were not paying attention, or that you did not realize that what was being explained was a way to achieve a modulus operation.

Modulus in x86 assembly can be obtained by dividing two numbers. You put the divident in some register, you execute some instruction supplying the divisor, and after the instruction has executed some register receives the quotient and another register receives the remainder. But that's irrelevant, because you have probably not learned the division operation yet, and that's okay, because we are not going to use division.

In x86 assembly (and in any other assembly for that matter) you can very easily calculate the remainder of a division by a power of two without having to use a division operation. And 2 is a power of 2. (The 1st power of 2.)

Actually, things become even simpler in the case of 2, because I hope you agree that it is self evident that the remainder of a division by 2 has only two possible outcomes: either 1 or 0.

As you might recall, a number in binary looks something like this: 0011101010 The leftmost bit is the most significant bit, and the rightmost bit is the least significant bit. And it is a fundamental property of the binary system of representing numbers that the least significant bit of a number always represents the remainder that you would receive if you divided that number by two. (Just as in the decimal system the rightmost digit represents the remainder that you would receive if you divided that number by 10.)

So, all you need to do is to isolate the least significant bit from the number. This will be 0 or 1, and it will represent the remainder of the division of the number by 2.

"Precisely how to achieve this is left as an exercise to the student."

(Give it a try, and if you cannot do it, post another stackoverflow question.)

Regarding the JE/JNE part, it is actually wrong, because when CX ceases to be less than 99 the JL Loop1 instruction will fall through to the ifwrong: label, which is not what you want. You should rewrite it as follows:

    JE iftrue
ifwrong: ;unnecessary label, for illustration purposes only
    SUB AX, CX
    JMP after
iftrue:
    ADD AX, CX
    JMP after ;unnecessary instruction, for illustration purposes only.
after:
    INC CX
    CMP CX, 99
    JL Loop1

Note that you do not exactly want ADD AX, CX and SUB AX, CX, you want ADD AX, MM and SUB AX, MM, where MM is CX modulus 2, and which you are still in the process of figuring out how to calculate.

Also note that I am not vouching as to whether CMP CX, 99 followed by JL Loop1 is correct, you have not asked about that, you will probably encounter it later, but it should be easy to figure out.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • Thank you so much for all the useful input. I'm aware of JMP but I was hesitant to use it because we haven't discussed it in class. Anyway, it seems like the best way to do things, if I intend on making the code organized. For the remainder dilemma, would it be possible to use `SHR MM, 1` to affect the carry value and then use `JC ifwrong` if there is carry and `JNC iftrue`? An alternative method that I thought of would be to `MOV MM, CX` then use `JPE ifwrong` and `JPO iftrue`? I can't confirm this myself because we haven't used any assembler to run our codes, just on paper and pen. – Axel1212 May 13 '17 at 21:35
  • 2
    The `SHR MM, 1` is one way to do it, but then you are going to temporarily need an extra register for that, (say, DX,) because you do not want to shift CX, because you don't want to destroy its value. The `TEST CX,1` instruction suggested by Aki Suihkonen is a better choice, as it tests bit 0 without changing anything. The `MOV MM, CX` won't work, because `MOV` instructions do not modify the flags register, so the parity flag won't be set. – Mike Nakis May 13 '17 at 21:52
  • But you are getting the hang of it, congratulations! C-:= – Mike Nakis May 13 '17 at 21:53
2

test cx,1 sets zero flag based on the last bit of CX. In this case Equal or JZ corresponds to cx % 2 == 0. Masking with x-1 is a special case of taking modulus and applies only when x is a power of two.

Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57