7
\$\begingroup\$

For an FASM assembly course I've to do the following exercise:

"Write a program that takes the number n as input. Then it prints all the numbers x below n that have exactly 2 different integral divisors (Besides 1 and x).

For example: 15 is such a number. It is divisible by 1,3,5,15. (Here 3 and 5 are the two different divisiors, besides 1 and 15).

However, 4 is not such a number. It is divisible by 1,2,4."

Full description can be seen here: GitHub xorpd.

Here's the code I've written:

format PE console
entry start

include 'win32a.inc' 

; ===========================================
; Write a program that takes the number n as
; input. Then prints all the numbers x below
; n that have exactly 2 different intergral 
; divisors (Besides 1 and x).
; Example: 15 : 1, 3, 5, 15 => 3 and 5.
; ===========================================

; ------ Usage example ----------------------
; The first "f" is the number n which was entered by the user.

; C:\Users\michael\Desktop\fasmProgs>divisors
; f
; 6
; 8
; a
; e
; f 
; ------------------------------------------

section '.text' code readable executable

start:
    call    read_hex            ; Provided by teacher. Reads in a hexadecimal number from stdin.

    mov     edi,    eax         ; edi becomes the upper limit.
    mov     esi,    5           ; esi becomes the control variable.

outerLoop:
    inc     esi
    mov     ebx,    2           ; ebx becomes the divisor and control variable of the inner loop.
    mov     ecx,    0           ; Counts how many division have been possible.
innerLoop:
    xor     edx,    edx
    mov     eax,    esi
    div     ebx

    cmp     edx,    0
    jne     prepareInnerIteration
    inc     ecx

prepareInnerIteration:
    inc     ebx                  ; Increment the divisor.

    cmp     ebx,    esi          ; Here the control variable acts as the upper limit.
    jl      innerLoop

prepareOuterIteration:
    cmp     ecx,    2
    jne     goToNextIteration

    cmp     esi,    6            ; In case no number was given. Just a blank.
    je      goToNextIteration

    mov     eax,    esi
    call    print_eax            ; Provided by teacher. Prints eax to stdin.    

goToNextIteration:
    cmp     esi,    edi
    jl      outerLoop

    push    0
    call    [ExitProcess]

include 'training.inc'

On of the tests I've done:

Testing with decimal 30 (hexadecimal 1e) as n.

I enter hexadecimal 1e (decimal 30).

It returns:

  • 6 => Can be divided by 2 and 3. Therefore correct detected.

  • 8 => 2, 4

  • 10 => 2, 5

  • 14 => 2, 7

  • 15 => 3, 5

  • 21 => 3, 7

  • 22 => 2, 11

  • 26 => 2, 13

  • 27 => 3, 9

I would say it works. At least essentially.

Nevertheless: There might be flaws even bugs in my program. And I guess one could improve it a lot.

So therefore:

Any hints, comments and recommendation by more experienced Assembly programmer very much appreciated.

\$\endgroup\$
10
\$\begingroup\$
mov ecx, 0    ; Counts how many division have 

This begs for an optimization in the form xor ecx, ecx. This will clear ECX just the same, is shorter and faster.


cmp edx, 0
jne prepareInnerIteration
inc ecx

Another optimization this time on compairing. To compare with zero, you can equally test the value with itself. So writing test edx, edx. This shortens the program.


goToNextIteration:
  cmp  esi, edi
  jl   outerLoop

Your code performs 1 iteration too many! Consider EDI=30 then this code will jump back to outerLoop when ESI=29, but there it will instantely be incremented to ESI=30 and so the code will superfluously process the case where the control variable = upper limit.


cmp  esi, 6            ; In case no number was given. Just a blank.
je   goToNextIteration

I don't see how this comment relates to the control variable being 6.
It just by-passes the output, which is wrong because the number 6 fulfills the conditions.


What I miss is a test for any user input smaller than 6

  call read_hex ; Reads in a hexadecimal number from stdin.
  cmp  eax, 6
  jl   ExitProg
  ...
ExitProg:
  push 0
  call [ExitProcess]

; ------ Usage example ----------------------
; The first "f" is the number n which was entered by the user.

; C:\Users\michael\Desktop\fasmProgs>divisors
; f
; 6
; 8
; a
; e
; f 
; ------------------------------------------

This is a misleading comment! Since the premise states that the results are to be below the inputted number, the line ; f conflicts in the usage example given.

\$\endgroup\$
  • 1
    \$\begingroup\$ All good comments. There is a slightly more advanced optimization that may also apply. As a general rule, I try to set up my loops to count 'down' from x to 0 rather then 'up' from 0 to x. The benefit is that instead of doing inc ebx; cmp ebx, esi; jl innerLoop I can do something like dec ebx; jnz innerloop (since dec already sets the flags for the jl). This not only saves me from the additional cmp instruction, I also don't necessarily need to keep esi laying around just so that I can compare it, which saves me a register. \$\endgroup\$ – David Wohlferd Feb 26 '17 at 23:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.