1

So I have this "calculator" in assembly and I have array num1 db 4 dup(0) for 4 numbers, lets say you enter 9 8 7 5, so 9 goes in position 0 of num1, 8 position 1 and so on... num1 = [9,8,7,6]

I also have:

bdec dw 1000d, 100d, 10d, 1d ;powers of 10 according to number position

num1h dw 0 ;word where I will sum the result

and I use this code to create a number out of digits inside an array base on power position.

I expect the code to do this: num1h = 9x10^3 + 8x10^2 + 7x10^1 + 5x10^0

NUM1P proc
        push ax
        push bx
        push cx
        
        mov di,0
        mov cx, 0
        mov ax, 0
        mov num1h,0
    
    power_base:
        mov al, [num1+di] 
        mov bx, [bdec+di]
        mul bx
        add num1h,ax
        inc cx
        inc di
        cmp cx,4
        je exitn1p
        jmp power_base

    exitn1p:

    pop cx
    pop bx
    pop ax

    ret
    endp    

When i print the result it gives me back 25852

Result

Now printing function works fine because it prints whatever you have in BX, so when I move a number directly to BX and use the printing function, let's say 5637 it will work fine:

        call NUM1P
    ;mov bx, num1h
        mov bx, 5637 
    call IMPRIME_BX
    jmp jmp_mouse_no_clic

Correct result

But when I try to transfer a number directly to bx using num1h, things get weird...

Any ideas on what might I be doing wrong?

Or any ideas on how can I debug a program with such an interface?

Adolf RJ
  • 19
  • 2
  • 1
    `bdec` is an array of words (`dw`) so you need to scale the index by 2. – Jester Jun 21 '23 at 10:32
  • @Jester Unfortunately scaling is not supported in 16bit mode, OP should use `DI` to index `num1`, and `SI` to index `bdec`. Also the register `AH` should be cleared at entry to `power_base`. – vitsoft Jun 21 '23 at 11:24
  • @vitsoft: You can't do scaling as part of the addressing mode, so you need other instructions. e.g. `mov bx, di` / `mov bx, [bdec+bx+di]`. Or like you say, a separate register with `add si, 2`, which also costs an extra 2 bytes of code-size and an extra insn in the loop, but also an extra instruction outside the loop. – Peter Cordes Jun 21 '23 at 14:51
  • Are you asking how to use the debugger? During debugging, first locate the addresses of your data. Inspect your data and its layout in memory, so you know what address holds the first word (1000), and also the second word (100), etc. Then when single stepping, observe the addresses in the registers and verify they refer to valid items in your array. Watch the load and store operations to see what they load. – Erik Eidt Jun 21 '23 at 15:24
  • Suggest you can change your code to use simpler addressing modes, manifesting pointer values in register so you can see them during debugging, so instead of `mov bx, [bdec+di]`, do the addition explicitly to a register, something like `mov dx,offset bdec; add dx,di, mov bx,[dx]`. That way you expose the addresses being used to a register you can observe in the debugger. – Erik Eidt Jun 21 '23 at 15:31

1 Answers1

0

Some bugs

  • While AH=0 the first time you execute mov al, [num1+di], that is no longer the case on the following iterations! You need that zeroing within the loop.

  • The bdec array is of type word and so you need to walk through that array in steps of 2 bytes. With that single inc di, your code currently traverses this array in steps of 1 byte only.
    Because the multiplication can use a memory operand directly, the easy way to achieve the correct result is:

      mov al, [num1+di]   ; Step of 1 byte
      cbw
      mov bx, di
      mul [bdec+di+bx]    ; Step of 2 bytes
    

Some improvements

  • You are preserving AX, BX, and CX registers. At the same time you do not preserve DI, but are you aware that the DX register got modified by the word-sized multiplication?

  • You should use the opposite conditional jump so you have less branching to do which is generally faster.
    A construct like:

      cmp  cx, 4
      je   exitn1p
      jmp  power_base
    exitn1p:
    

    would better become:

      cmp  cx, 4
      jne  power_base
    
  • You don't need a separate loop control variable CX, because DI contains identical values and can play that role just as easy.

  • You can replace mov num1h, 0 by the 2 bytes shorter mov num1h, di because the DI register is known to contain 0.

  • You can replace mov di, 0 by the 1 byte shorter xor di, di that equally zeroes the DI register.

New code

  push ax
  push bx
  push dx
  push di
  xor  di, di
  mov  num1h, di       
power_base:
  mov  al, [num1+di]   ; -> AL=[0,9]
  cbw                  ; -> AX=[0,9]
  mov  bx, di
  mul  [bdec+di+bx]    ; Step of 2 bytes since DI is equal to BX
  add  num1h, ax
  inc  di
  cmp  di, 4
  jne  power_base
  pop  di
  pop  dx
  pop  bx
  pop  ax
  ret
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • 1
    `bdec` is just an array of powers of 10. A more efficient algorithm would use `total = total*10 + digit` so you only multiply by a constant 10, which can optionally be done without `mul` if you care about performance on old CPUs. But yes, this would probably be a good fix with minimal changes to the OP's code, keeping the very inefficient memory-destination `add` inside the loop and everything. Apparently clobbering DI is ok for their function, but not AX; weird. – Peter Cordes Jun 21 '23 at 21:34
  • [NASM Assembly convert input to integer?](https://stackoverflow.com/q/19309749) is a 32 or 64-bit version; 16-bit is different since scaled LEA isn't available. And without 186, `imul dx, 10` isn't available. IDK if there's a good 16-bit canonical answer for this. – Peter Cordes Jun 21 '23 at 21:39