1

我正在编写一个程序来打印从 0 到 100 的所有数字,并且我需要找到一个变量(在本例中为变量counter)所持有的位数。

这是我的代码:

SECTION .data
len EQU 32

SECTION .bss
counter resd len 
digit1 resd len
digit2 resd len
digit3 resd len

SECTION .text
GLOBAL _start
_start:
nop

Print:
mov eax, 4
mov ebx, 1
mov ecx, counter
mov edx, len
int 80h 

Set:
mov BYTE [counter], 1

Divide:
; HERE IS WHERE I NEED TO FIND THE LENGTH OF THE VARIABLE COUNTER
; initial division
mov ax, [counter]   ; number we want to print
mov ch, 10    ; we divide by ten to siphon digits
div ch        ; divide our number by 10

; al now has 11, ah has 1
mov dh, ah             ; save the remainder in dh
xor ah,ah
mov ch, 10             ; refill ch with the divisor
div ch                 ; al now has 1, ah now has 1

Move:                     ; now to move our digits to a printable state
mov [digit1], dh      ; first digit is in edx
mov [digit2], ah
mov [digit3], al

Adjust:
add BYTE [digit1], '0'
add BYTE [digit2], '0'
add BYTE [digit3], '0'

Print:
mov eax, 4
mov ebx, 1
mov ecx, digit1
mov edx, len
int 80h

mov eax, 4
mov ebx, 1
mov ecx, digit2
mov edx, len
int 80h

mov eax, 4
mov ebx, 1
mov ecx, digit3
mov edx, len
int 80h

Exit:
mov eax, 1
mov ebx, 0
int 80h

我需要找到长度,以便知道要划分多少次以及打印变量计数器的位数。

我怎样才能找到它有多长?

提前致谢

4

3 回答 3

1

对于 range 中的数字0..100,我只需在边界处进行比较,伪汇编器如下:

    mov ax, [counter]

    mov cx, 3             ; default length
    cmp ax, 100           ; >= 100, use 3
    bge done

    dec cx                ; set length to 2
    cmp val, 10           ; >= 10, use 2
    bge done

    dec cx                ; set length to 1

done:
                          ; cx now holds the digit count.

这实际上最多可以处理 999 个,但如果您想扩大范围,您还可以在 100 个之前添加更多条件检查。

于 2012-10-14T07:27:12.783 回答
1

阿基举了个例子!用“-S”开关编译它,看看编译器用它做了什么——尽管这可能没有太大帮助。:)

你问如何算出位数。我见过代码(来自一本著名书籍的作者!),它执行一个完整的div循环,直到商为零,只是为了计算数字。然后他做了另一个div循环来找到他们!执行 >10、>100、>1000 等将起作用。但为什么你需要知道?您可以一直保持div直到商为零,然后计算数字。实际上,您可以通过将商与 9 进行比较来保存div(div非常慢!) - 如果be您的最后一位数字在 al 中。divIMO ,做额外的事情更简单。:)

我们以“错误”的顺序得到余数/数字。我知道解决这个问题的三种方法。我认为“最简单”的是将它们推入堆栈,对它们进行计数,然后按照我们想要存储/打印它们的顺序将它们弹出,在之前或之后添加“0”。

另一种方法是从缓冲区的“后部”开始并“向前”工作。这就是阿基的颂歌所做的。对于 C 兼容代码,返回一个以零结尾的字符串很好,但 sys_write 不知道以零结尾的字符串,它想知道长度。(没问题,我们可以找到“zstring”的长度)请注意,这不会到达缓冲区的开头。如果您不关心“C 兼容”代码,您可以返回“我们在缓冲区中到达的位置”和长度 - 也许在sys_write 想要的位置ecx和位置。edx或者您可以空格填充到缓冲区的开头。右对齐的数字打印在列中看起来不错。:)

还有一种方法是继续将它们“向后”放入缓冲区并在最后做一个“反向字符串”。

这些方法都不是很快。div很慢,期间!我“拥有”一些来自 Terje Mathieson(“光速”解决方案专家)的代码,这些代码不使用div但我不理解也不使用它。如果您赶时间,可以在各种优化手册中找到它。:)

将自己限制为 100 并不会带来太多好处。使用完整的 32 位寄存器编写例程同样容易(也许更容易),并且 CPU 对此很满意。如果您需要处理负数,那只会稍微复杂一些。如果您在此处找不到示例,可以在 Nasm 论坛上找到一些示例,但您可以自己尝试一下,就像您正在做的那样,这是一个有趣的练习。

呃...看...保留“太多”内存可能会使您的程序“臃肿”,但不会造成任何伤害。保留“不够”的内存是一个错误!所以你最好在“太多”方面犯错。仍然...

SECTION .data
len EQU 32

SECTION .bss
counter resd len 
digit1 resd len
digit2 resd len
digit3 resd len

...使用一个 128 字节的缓冲区,并为单个数字/字符打印 32 个字节,似乎很多。:)

最好的,弗兰克

于 2012-10-14T14:03:59.690 回答
0

通常这是通过内部缓冲区完成的(实际上堆栈会这样做:)

你除以 Y=X mod 10(或 Y=X mod base),X=X div base,直到 X=0(并压入每个 mod Y)计算除数,然后从堆栈 C 中弹出每个结果的次数并写入输出流。

void print_number(int eax, int ebx) {  // ebx = base, eax = number
   int ecx = 0;
   do {
      edx = eax % ebx;
      eax = eax / ebx;
      push(edx);
      ecx++;
   } while (eax);

   while (ecx) {
      pop (eax);
      putch(eax+'0');
      ecx--;
   }
}

关键是您最终会根据需要进行多次划分。

稍微优化一点的事情是[再次在 C 中鼓励您自己的思考]...

void print_number(int a, int base) {
     char string[10];
     static *ptr = string+9;  // points to the last char of string
     *ptr--=0;                // write ending ASCII Zero.
     *ptr='0';
     while (a) {
          *--ptr= '0'+(a % base);  // works for base = 2-10
          a/=base;
     }
     printf("%s",ptr);
}

你能找出为什么这有效(或无效)吗?

于 2012-10-14T07:33:47.193 回答