# Assembly program which detects all numbers with two integral divisors

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
; ------------------------------------------

start:

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:

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.

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.

• 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. Feb 26, 2017 at 23:40