2

So, starting from an array of words, I have to create an array to include the digits of each given word, written in base 10.
So if I have

s DW 12345, 20778, 4596 

the result should be this BYTE array

t DB 1, 2, 3, 4, 5, 2, 0, 7, 7, 8, 4, 5, 9, 6

I've been suggested how to do it, I've tried implementing it, but I get the following errors

Argument to operation or instruction has illegal size"

(regarding the "push al" and "pop al" lines)

Here's the code I've tried implementing:

ASSUME cs:text_,ds:data_

data_ SEGMENT

s dw 12345, 20778, 4596
l equ $-sir 
d db (l/2)*5 dup (?)

data_ ENDS

text_ SEGMENT
start:
    mov ax, data_
    mov ds, ax
    mov es, ax
    mov si, offset s 
    mov di, offset d
    mov cx, l    ;storing the length of s
    mov dl, 10   
    mov ax, [si] ;storing the data of si into ax so that the division can be made
    cld          ;setting the direction flag
    jmp loop1    
    cloop1:
        div dl   ;divide by 10 so we can get the remainder
        push al  ;ERROR LINE ;my plan was to store the value of al into the stack, so I can store the remainder into al
        mov al, ah
        stosb    ;we add the remainder to the final line
        pop al   ;ERROR LINE ;my plan was to get the value of al from the stack and do the instruction once againq
    loop cloop1
    loop1:        ;the role of this loop is to repeat the instruction as long as there are words left (similar to while instruction in C++)
        cmp ax, 0  
        jg cloop1  ;if there are words left, the code keeps on being executed 
    loop loop1
    mov ax, 4c00h
    int 21h
    text_ ENDS
    end start   

And here's the idea it's based on (represented in C++):

nr=0;
while(x>0)
 {c=x%10;
  nr=nr+x*10;
  x=x/10;
 }
cout<<nr;

Thanks in advance and sorry for any mistakes. Any advice regarding my problem would be much appreciated

zx485
  • 28,498
  • 28
  • 50
  • 59
Katrin
  • 57
  • 7
  • 1
    This code has also other fundamental problem: `12345 / 10 = 1234` and `1234` will not fit into 8 bit `al`, so you will get division error. You will have to modify the code to divide 32 bit (`dx:ax` i.e. do `xor dx,dx` to have `dx=0`) value by 16 bit `10` (for example `bx`). Then the remainder will land in `dx` and quotient in `ax`, so you will have to rewrite the whole loop code a bit. – Ped7g Dec 04 '17 at 14:22
  • @Ped7g after posting the question I noticed that problem as well. Thank you! Will change it right away – Katrin Dec 04 '17 at 14:31
  • @Ped7g since I'll have the quotient in ax and the remainder in dx, would it be a good idea to interchange them so I could use STOSW to store the quotient into di? – Katrin Dec 04 '17 at 17:31
  • it's "working" idea, but not excellent one. As you write only single byte into memory and you will need quotient as base of next dividend (ie. `ax` -> `ax` again), the straightforward "manual stosb" `mov [di],dl` `inc di` is much faster and simpler, than the: swap, stosb, swap back .. btw, stosB, if you want to keep digits as bytes. Plus with normal `mov [di]` you don't need to set `es` (if you don't use it elsewhere). – Ped7g Dec 04 '17 at 17:41

2 Answers2

1

You can't push an 8 bit register onto the stack. In 16 bit mode, you can only push and pop 16 or 32 bit registers. Try to replace push al and pop al with push ax and pop ax.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • considering that I have to constantly update the value in AH (the quotient), will push/pop ax do it or should I find an alternative? – Katrin Dec 04 '17 at 14:01
  • @Katrin `push ax` pushes all of ax, that is, both ah and al. Similarly, `pop ax` restores all of `ax`, that is both `ah` and `al`. If this is not suitable for your desires, consider moving the content of `al` to a different register such as `bl` instead. – fuz Dec 04 '17 at 14:04
1

Your C loop is sane except for nr=nr+x*10; I think you're converting back into an integer with reversed order of decimal digits? That's not a useful way to think about it in asm, because you don't have a cout << nr library function available.

The usual strategy (especially if you just need to print as a string) is to start with a pointer to the end of a buffer and work towards the front. (e.g. dec di / ... / div / add dl, '0' / mov [di], dl / test ax,ax / jnz). See for example How do I print an integer in Assembly Level Programming without printf from the c library? for an x86-64 version. Porting the digit-conversion loop to x86-16 with 16-bit integers is straightforward; I didn't use any x86-64 specific stuff.

So you exit the loop once your quotient becomes 0; you're done and you have your digits in printing order. The digits are from wherever your current pointer is (di) to the end of the buffer. Feed that to a print-string function (I think int 21h has one of those).

If you want the result in an array with fixed start position, you could just copy. But if you only require all the digits as integers in a contiguous buffer, you can get that. Start with the last word at the end of your allocated space for your buffer. When you get all the digits from a word, move to the next word (sub si,2) but don't move di, so the digits from the next word are stored right before the digits from the higher-address word. Or if you're optimizing for code-size, use lodsw and stosb.

When you're done the final word of your source array, di is pointing to the start of an array of digits.


Push as you generate and then pop in a loop is another way to deal with the div/mod algorithm generating digits in least-significant-first order. But it needs a separate loop to pop. And as @Fuz points out, you have to push/pop a whole 16-bit register.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847