Search code examples
assemblyx86emu8086

8086 assembly language program to find number of odd and even numbers in an array of 16-bit hexadecimal numbers


In order to determine if the hexadecimal no is even, the program would divide the number by 2 and the remainder should be equal to zero. If not, then it is an odd number. Initially, my plan was to I have a variable or register that will increment when a hexadecimal is proven to be even. Then afterwards I would just subtract the number of even nos. to five to get the number of odd nos. But since I had to use three registers to hold the dividend and divisor (AX & BX), plus another for the array counter (CX), I ran out of registers to hold the value of the "even counter".

I revised the program so that it can still satisfy the instruction (see title). This time, the program would display the character E for even and O for odd. My problem now is the program only recognizes up to the second array item, meaning the value of SI remains the same and does not increment after the second array item. This makes the output of the program EOOOO instead of EOOOE.

My questions are: 1.) How will I increment the value of SI from its memory and pass it to AX to be divided 2.) Is it possible to make my initial plan work? If yes, what register can I use to hold the "even counter"

Here is the code:

.MODEL SMALL
 
READ MACRO MSG
    MOV AH,0AH
    LEA DX,MSG
    INT 21H
ENDM

SET MACRO MSG
    MOV AH,09H
    LEA DX,MSG
    INT 21H
ENDM

.DATA 
 
 CR EQU 0DH
 LF EQU 0AH
    ARR DW 100h,16Fh,191h,10Fh,120h 
    MSG1 DB CR,LF,"Array of 16 bit hexadecimals: 100h,16Fh,191h,10Fh,120h$"
    MSG2 DB CR,LF,"E=EVEN   O=ODD$"
    
    NUMERALEVEN DB CR,LF,"E$"
    NUMERALODD DB CR,LF,"O$"
    
 COUNT EQU 5H
 

DATA ENDS

    
.CODE
ASSUME CS:CODE,DS:DATA 

START:
        MOV AX,DATA
        MOV DS,AX
    
        SET MSG1
        SET MSG2
        
        MOV SI,OFFSET ARR
        MOV CL,COUNT
        MOV AX,[SI]
        
        MOV DX,0000

                
CHECK:  
        MOV DX,0000                       
        MOV BH,00
        MOV BL,02H     ;divide by 2  
        DIV BX         
        
        CMP DX,0       ;checks if there is a remainder by comparing the remainder to 0
        JE EVEN
        JNE ODD        
        
        
EVEN:   
        SET NUMERALEVEN
        
        MOV DX,00 
        DEC CL
        MOV AX,[SI+1]
        MOV [SI],AX 
        
        CMP CL,0
        JNZ CHECK
        
         
ODD:    
        SET NUMERALODD
        
        DEC CL

        MOV AX,00
        MOV AX,[SI+1]
        MOV [SI],AX
        
        CMP CL,0
        JNZ CHECK
                
CODE ENDS
END START 

Solution

  • This makes the output of the program EOOOO instead of EOOOE.

    You seem not to be fully aware what the following instructions in your code actually do:

    MOV AX,00
    MOV AX,[SI+1]
    MOV [SI],AX
    

    The first instruction is useless because the second instruction will overwrite the value of AX.

    Because your array consists of 16-bit values and x86 CPUs use byte-wise addressing (like most but not all CPUs), the second value will be stored at the address [SI+2], not at [SI+1].

    By reading [SI+1], you read some (non-sense) value which can be calculated from the first two numbers in the array.

    And using the last instruction you overwrite the first number in the array, which makes no sense to me.

    You never modify the SI register. Without modifying the SI register, you will never read values later in the array!

    So what your program does is the following:

       Initially, your array contains the values 100h,16Fh,...
       You check the value 100h (if it is even or odd)
     MOV AX,[SI+1]   loads 6F01h into AX
     MOV [SI],AX     overwrites the first value in the array by 6F01h
       You check the value 6F01h (if it is even or odd)
     MOV AX,[SI+1]   loads 6F6Fh into AX
     MOV [SI],AX     overwrites the first value in the array by 6F6Fh
       You check the value 6F6Fh (if it is even or odd)
     MOV AX,[SI+1]   loads 6F6Fh into AX
     MOV [SI],AX     has no effect ...
       ... because the first element of the array is already 6F6Fh
     MOV AX,[SI+1]   loads 6F6Fh into AX
       ...
     MOV AX,[SI+1]   loads 6F6Fh into AX
       ...
     MOV AX,[SI+1]   loads 6F6Fh into AX
       ...
    

    So the first time your program checks if 100h is even or odd. This is what you want.

    The second time it checks if 6F01h is even or odd. And the third, fourth, fifth ... tenth ... hundredth time, it checks if 6F6Fh is even or odd. This is what you probably don't want.

    In order to determine if the hexadecimal no is even, the program would divide the number by 2 and the remainder should be equal to zero.

    If you work with decimal numbers (without a computer) and want to check if a number is divisible by ten - would you also divide the number by ten and look at the remainder?

    No. You would look at the last digit and check if it is zero.

    You can do this in all numeral systems:

    In ternary system, a number is divisible by 3 if the last digit is zero; in hexadecimal system it is divisible by 16 if the last digit is zero. And in binary system it is divisible by two if the last digit (= bit) is zero.

    You can use the TEST instruction to check if a bit is zero or one:

    The instruction TEST xxx, 1 checks if the last bit of the value xxx is 0 and set the zero flag if this is the case (and clear the zero flag otherwise). This means that the JZ instruction (which is the same as JE) will jump if that bit was zero. And JNZ (JNE) will jump if that bit was one.

    So you can replace the following code by the instruction TEST AX, 1:

    MOV DX,0000
    MOV BH,00
    MOV BL,02
    DIV BX
    CMP DX,0
    

    And you can even use TEST WORD [SI], 1 to directly check the bit in a number from the array, so you don't even need to use MOV AX, [SI] to load the value into the AX register.