【问题标题】:Assembly Language Loop Not working汇编语言循环不起作用
【发布时间】:2016-03-02 07:17:14
【问题描述】:

我是汇编语言的初学者。我想用空格打印 1-9。我想这样打印

1 2 3 4 5 6 7 8 9

这是我正在使用的代码ma​​sm此代码挂起命令提示符

为什么这不起作用?

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    

【问题讨论】:

  • 找到好例子的一个有用技巧是用 C 编写你想要做的事情并使用编译器输出汇编,然后看看它是如何完成的。
  • @slashmais 非常棒的把戏

标签: assembly masm


【解决方案1】:
MOV AH,02
MOV AX,'0'   ; sets ah=0.  this is your bug (there may be others).

以下循环打印所有数字,但不留空格。我会把它留给你。 (编辑:哎呀,这会打印从 0..9 开始的数字,因为这就是您的代码在系统调用之后对 inc 所做的事情,并且以“0”开头。显然,请改用 '1' 开头。)

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

假设 int 21 / ah=2 打印 dl 中的字符。 int 21 不会破坏任何寄存器 (except the return value in al),因此您不需要在循环内使用 mov dx, ax。 (编辑:是的,因为如果您一次打印一个字节,则需要交替空格和数字)。

使用 AH=09h 编写整个字符串意味着您可以有效地构造它,然后打印整个字符串。例如

    ;; 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

    ...

请注意,与大多数 asm 示例不同,此代码不使用静态缓冲区(bss 或数据部分)。这可能是因为细分。不要花太多时间学习段,它们对于现代操作系统下的现代程序没有用。请参阅 wiki。

还要注意它不使用loop,因为that's slow

我本可以使用push 在堆栈上创建字符串,但这可能更令人困惑,而且您永远不会看到编译器会这样做。使用推送,它会是这样的

    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

不过,这会在 9 之后留下一个尾随空格。

【讨论】:

  • 您缺少数字之间的空格 ;)
  • @Tommylee2k:谢谢,已修复。
  • MOV AX,'0' ; sets ah=0 - 不,它没有!最好使用xor ax,ax 将斧头归零
  • @slashmais:AH 是 AX 的上半部分,0 作为 16 位常量是 0x0030。设置AX=0x0030 同时设置ah=0al='0',一条指令。那是 OP 的代码,也是他的 bug 的来源(在存储电话号码后破坏了 AH)。我故意在我的代码中使用它,因为它节省了代码字节和指令数量。
  • 是的,你是对的,看到斧头并继续看到斧头 - 冲浪评论犯罪案件
【解决方案2】:

哇,我用简单的逻辑打印它。但我认为这不是一个有效的解决方案。

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    

【讨论】:

  • 这很合理,只是你将ONESPACE 保留在内存中。如果可以的话,永远不要将循环计数器保存在内存中;更喜欢在循环中溢出一些只读的东西。您可以继续用cmp dl, '9' / jle L1 替换loop 指令,然后您不需要将cx 与循环计数联系起来。所以mov dl, cl / int21 / mov dl, ' ' / int21 / inc cl / cmp cl, '9' / jle L1 应该可以解决问题。 (在循环外进行适当的设置)。
  • The loop instruction is slow, maybe intentionally so for use in delay-loops。仅将它用于代码大小,并且当它自然适合您的循环时(您想向下计数)。当您已经需要其他可以用作循环计数器的东西时,不值得牺牲寄存器。
  • 当然,任何使用 16 位代码和 int 21h 的东西本质上都不是有效的解决方案。 16 位 DOS 不是学习 ASM 的最佳方式。但是我们的解决方案在效率上的最大区别不在于是否将变量保存在寄存器中,而是int 21h 调用的数量。系统调用比往返 L1 缓存(或真正通过存储转发)要昂贵得多。所以我的版本构建一个字符串然后进行一次系统调用来打印它可能比完美优化的一次一个字符循环快 10 倍。
  • 无论您使用什么系统调用 API(16 位 DOS、64 位 Linux 等)都会出现这种情况。但是学习 DOS 系统调用 API 有点傻,因为您不会当你尝试用 asm 做任何“真正的”事情时使用它。没有人想要新的 DOS 程序。
  • 感谢@PeterCordes 真的很有帮助
猜你喜欢
  • 2015-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
  • 2012-01-08
  • 1970-01-01
相关资源
最近更新 更多