3

I am beginner in assembly language.I want to print 1-9 with spaces. I want to print like this

1 2 3 4 5 6 7 8 9

Here is my code I am using masm this code hangs command prompt.

Why this is not working?

DATA_SEG SEGMENT
DATA_SEG ENDS
CODE_SEG SEGMENT 
        ASSUME CS:CODE_SEG , DS:DATA_SEG
MAIN PROC FAR
    MOV AH,02
    MOV AX,'0'
    MOV CX,10
L1:
    MOV DX,AX
    INT 21H
    INC AX
    LOOP L1

    MOV AX,4C00H 
    INT 21H
    MAIN ENDP
CODE_SEG ENDS
END MAIN    
Codor
  • 17,447
  • 9
  • 29
  • 56
Syed Qasim Ahmed
  • 1,352
  • 13
  • 24

2 Answers2

3
MOV AH,02
MOV AX,'0'   ; sets ah=0.  this is your bug (there may be others).

The following loop prints all the digits, but doesn't leave spaces. I'll leave that up to you. (edit: oops, this prints digits from 0..9, because that's what your code was doing with the inc after the system call, and starting with '0'. Obviously start with '1' instead.)

    MOV   AH, 2
    mov   dl, '0'
.l1:
    INT   21H
    INC   dl
    cmp   dl, '9'
    jle .l1

assuming int 21 / ah=2 prints the character in dl. int 21 doesn't clobber any registers (except the return value in al), so you don't need to mov dx, ax inside the loop. (edit: yes you do, since you need to alternate spaces and digits if you're printing one byte at a time).

Using AH=09h to write a whole string would mean you could construct it efficiently and then print the whole thing. e.g.

    ;; Not debugged or tested (I don't keep DOS around...)
    ;; So I may have gotten some 2-character constants in the wrong order
    sub    sp, 22      ; reserve space (slightly more than necessary because I didn't debug or even test this)
    mov    di, sp
    mov    ax, '0 '
.l1:
    stosw               ; store digit and trailing space into [edi+=2]
    inc    al
    cmp    al, '9'
    jle   .l1

          ; note that NASM doesn't process \ escapes, but MASM does
    mov    word [di], '\n$'   ; newline and DOS end-of-string marker
    mov    dx, sp             ; dx=input param = start of our buffer
    ; this only works if DS=SS, I guess.  Yay segments.
    mov    ah, 09h            ; DOS write string syscall
    int    21h
    ...
    add    sp, 22             ; release the stack buffer

    ...

Note that this code doesn't use a static buffer (bss or data section), unlike most asm examples. This is probably because of segments. Don't spend much time learning about segments, they're not useful for modern programs under modern OSes. See the wiki.

Also note that it doesn't use loop, because that's slow.

I could have created the string on the stack with push, but that's maybe more confusing and something you'd never see a compiler do. With push, it would be something like

    push   '\n$'
    mov    ax, '9 '
.l1:
    push   ax       ; like stosw in reverse, but with SP instead of DI
    dec    al
    cmp    al, '0'
    jge    .l1

    ... make the system call with dx=sp

This leaves a trailing space after the 9, though.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    you're missing the spaces inbetween the digits ;) – Tommylee2k Mar 02 '16 at 07:28
  • @Tommylee2k: Thanks, fixed. – Peter Cordes Mar 02 '16 at 07:44
  • 1
    `MOV AX,'0' ; sets ah=0` - no it doesn't! better to use `xor ax,ax` to zero ax – slashmais Mar 02 '16 at 07:50
  • @slashmais: AH is the upper half of AX, and `0` as a 16bit constant is `0x0030`. Setting `AX=0x0030` sets `ah=0` and `al='0'` at the same time, with one instruction. That was the OP's code, and the source of his bug (clobbering AH after storing the call number). I used it intentionally in my code because it saves code bytes and number of instructions. – Peter Cordes Mar 02 '16 at 07:59
  • @PeterCordes: Nice answer but you don't explain the error he made: `mov ax, '0'` modifies `ah` as `ah` is the high byte of `ax` and therefore the parameter given to the interrupt are not the one he expects. Hence the solution to put the char directly in `dx`, where it belongs. – Colin Pitrat Mar 02 '16 at 08:01
  • Ah sorry, you do say it in the comment you added to the code (but it's not very detailed :-)). I just missed it ! – Colin Pitrat Mar 02 '16 at 08:02
  • @ColinPitrat: yeah, re-reading the early part while replying to slashmais, I already saw that it didn't stand out now that I made the rest of the answer so big. Fixed :) I wasn't a fan of the OP's loop (wasting time on setting CX and using `loop`), so I felt like writing a cleaner version. Basically to prove to myself that `loop` doesn't have much in the way of upsides: slow, and doesn't improve readability. I often see 16bit ASM in SO questions doing every loop with the `loop` instruction. It's only a natural fit in some cases; in others it wastes an extra register. – Peter Cordes Mar 02 '16 at 08:03
  • 1
    yes, you're right, saw ax and continued to see ax - case of surf-by-commenting crime – slashmais Mar 02 '16 at 08:09
0

Wow I print it with simple logic.But I think it is not an efficient solution.

DATA_SEG SEGMENT
ONE DW 48
SPACE DW 32
DATA_SEG ENDS
CODE_SEG SEGMENT 
        ASSUME CS:CODE_SEG , DS:DATA_SEG
MAIN PROC FAR

    MOV AX,DATA_SEG ; TO INTIALIZE DATA SEGMETN
    MOV DS,AX


    MOV AH,02

    MOV CX,10
      L1:

    MOV DX,ONE
    INT 21H
    MOV DX,SPACE
    INT 21H
    INC ONE
             LOOP L1

    MOV AX,4C00H 
    INT 21H
    MAIN ENDP
CODE_SEG ENDS
END MAIN    
Syed Qasim Ahmed
  • 1,352
  • 13
  • 24
  • 2
    That's pretty reasonable, except that you're keeping `ONE` and `SPACE` in memory. Never keep loop counters in memory if you can help it; prefer spilling something that's read-only in the loop. You could keep replace the `loop` instruction with `cmp dl, '9'` / `jle L1`, and then you don't need to tie up `cx` with the loop count. So `mov dl, cl` / int21 / `mov dl, ' '` / int21 / `inc cl` / `cmp cl, '9'` / `jle L1` should do the trick. (with appropriate setup outside the loop). – Peter Cordes Mar 02 '16 at 13:39
  • 1
    [The `loop` instruction is slow, maybe intentionally so for use in delay-loops](http://stackoverflow.com/questions/35742570/why-is-the-loop-instruction-slow-couldnt-intel-have-implemented-it-efficiently). Only use it for code-size, and when it's a natural fit for your loop (you want to count downwards by one). It's not worth sacrificing a register when you already need something else that works as a loop counter. – Peter Cordes Mar 02 '16 at 13:41
  • 1
    Of course, anything that uses 16bit code with `int 21h` is inherently not an efficient solution. 16bit DOS is not the best way to learn ASM. But the big difference in efficiency between our solutions isn't keeping a variable in a register or not, it's the number of `int 21h` calls. A system call is vastly more expensive than a round-trip to L1 cache (or really through store-forwarding). So my version that builds a string and then makes one system call to print it will probably be 10 times faster than a perfectly-optimized one-char-at-a-time loop. – Peter Cordes Mar 02 '16 at 13:46
  • 1
    This will be the case regardless of what system call API you're using (16bit DOS, 64bit Linux, etc.) But learning the DOS system call API is kind of silly, because you won't be using it when you try to do anything "for real" with asm. Nobody wants new DOS programs. – Peter Cordes Mar 02 '16 at 13:48
  • Thanks @PeterCordes really helpfull – Syed Qasim Ahmed Mar 02 '16 at 15:53