Search code examples
assemblymasmemu8086

EMU 8086 stores wrong array element value in register


.model small
.data
nizA db 1,2,3,4,5,6,7,8   
nizB db 8 dup(?)
len equ 8
.code   
main proc
mov si,0
mov di,0
mov cx,len

program: 
    mov al,nizA[si]      ;problem is here it always stores CDh in AL
    cbw                  ;convert AL to AX so i can divide
    mov bl,2             ;The number I want to divide so I test if its even or not
    div bl               ;Overflow message
    cmp ah,0
    je next:
        input:
            mov dl,nizA[si]
            mov nizB[di],dl
            inc di
next:
    add si,2
    loop program
endp
end

I have this code here, for my finals that I need to make, very simple. Find odd numbers from even indexes of given array. So naturally it means I increase SI by 2 and I put that array element in AL. But no matter what number is in array, what gets stores in AL is always hex number CD, which then, if it was stored as it was supposed to, I want to do CBW to convert to AX and be able do divide with BL I now just have error message from EMU 8086 saying

divide error - overflow. to manually process this error, change address of INT 0 in interrupt vector table.

Which surely is related to CD number, I only have two problems here

  1. CD being stored instead of 1
  2. Overflow (most likely because CD is way too high, but still I divide the numbers they only go less so it wont make sense kinda)

Solution

  • "CD being stored instead of 1"

    When a DOS .EXE starts, the DS segment register does not point at your program's .data section! The DS segment register points at the ProgramSegmentPrefix aka PSP. This 256-byte area is where DOS keeps some important data about the running program, and where you can retrieve the program's command line at offset address 128.

    You found the value CDh when your code ran because that happens to be the very first byte in the PSP. It is the opcode for the int 20h instruction that always begins the PSP.

    What you need to write is the following:

    .code   
    main proc
    mov ax, @data
    mov ds, ax
    

    "Overflow (most likely because CD is way too high, but still I divide the numbers they only go less so it wont make sense kinda)"

    Actually it does make sense. Because you are using CBW to extend the dividend into AX, and because the value in AL erroneously is CDh, the new value in AX becomes FFCDh. When your code then divides this by 2 (using mov bl, 2 div bl) the quotient is much larger than what can be stored in the division's dedicated quotient register AL. That's why you get the "divide error".

    Solutions

    • Solution to verify that the division works this time:
        mov  ax, @data
        mov  ds, ax
        mov  si, 0
        mov  di, 0
        mov  cx, len/2
    program: 
        mov  al, nizA[si]
        mov  ah, 0           ; For UNSIGNED division don't use CBW
        mov  bl, 2
        div  bl
        cmp  ah, 0           ; Remainder
        je   next
        mov  dl, nizA[si]    ; Reload
        mov  nizB[di], dl
        inc  di
    next:
        add  si, 2
        loop program
    
    • Solution that avoids division in favor of TEST and adding some extra improvements too:
        mov  ax, @data
        mov  ds, ax
        xor  si, si        ; Better than 'mov si, 0` for zeroing a register
        xor  di, di        ; idem
    program: 
        mov  al, nizA[si]
        test al, 1
        jz   IsEven
        mov  nizB[di], al  ; Only storing 'odd' values
        inc  di
    IsEven:
        add  si, 2         ; Next 'even' index
        cmp  si, len
        jb   program
    

    See how TEST AL, 1 is non-destructive on the AL register and that therefore you don't need to reload the value before writing to the nizB array?
    Also note that you don't always need a separate loop counter. Here I have used the source array index instead.