Assembly 在程序集中打印0-9

Assembly 在程序集中打印0-9,assembly,i386,Assembly,I386,我是这门语言的新手,我正在努力学习它 这是我第一次接触低级语言 这是我的不完整代码: ;------------Block 1---------- .386 .model flat,stdcall option casemap:none ;------------Block 2---------- include windows.inc include user32.inc includelib user32.lib include kernel32.inc includelib kerne

我是这门语言的新手,我正在努力学习它

这是我第一次接触低级语言

这是我的不完整代码:

;------------Block 1----------
.386
.model flat,stdcall
option casemap:none


;------------Block 2----------
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib


;------------block 3----------
.data
first DW 1 ; increment this value


;------------Block 4----------
.data?
retvalue dd ?


;------------Block 5----------
.code
start:
mov ecx,10
mov eax, '1'

l1:
    nop  ;Add code here
loop l1



xor eax,eax
invoke ExitProcess,eax        
end start
它的64位体系结构采用英特尔处理器

我参考了这些文章,但它们似乎都不适合我的代码体系结构

这已经让我烦了4个小时了


任何帮助都将不胜感激。

与人交朋友可能是一项挑战。主要是因为给了您非常基本的构建块来处理、内存段、要使用的段中的地址、要加载值并从中获取结果的处理器寄存器、基本系统调用,以及调用这些系统调用以操作处理器寄存器中当前值的方法。那么剩下的就由你来做了。我将试着给你一个大致的思想过程概述,你可以在下面处理基本的汇编调用

有许多web文章提供了一小部分谜题,但很少有文章提供了对该语言的合理完整的概述。这就是我在评论中提到的原因,我将在这里再次提及。如果您花时间阅读这些部分,您将很好地掌握如何使用assembly

在组装中,通常有两种方法来处理任何问题。打印
0-9
也不例外。在我的评论中,我解释了
0-9
的数值与可打印ASCII字符
'0'-'9'
之间的差异。为了向屏幕输出值,必须将ASCII值写入
stdout
(或文件描述符编号
1
,其中
stdin-0
stdout-1
stderr-2

要将值写入标准输出,必须在适当的处理器寄存器中使用适当的值进行适当的
sys\u write
系统调用。(您可以在
unistd_32.h
(32位)或
unistd_64.h
(64位)中找到合适的系统调用,它们位于依赖于发行版的include目录中,通常是
/usr/include/asm
/usr/include/asm-x86
)您只需要为自己的需要担心2个,
sys write
(number
4
)和
sys\u exit
(编号
1

什么进入哪个处理器寄存器?您知道系统调用号将进入第一个寄存器
eax
。谢天谢地,对于所讨论的命令,您通常可以从C
手册页
中找出其余部分。对于编写
man 2,写入页面将有所帮助(您可以以类似方式将手册页面用于所有系统功能)。查看
write
函数声明。寄存器值通常是函数(按顺序)所需的参数。e、 g

现在您知道了在每个寄存器中写入一个字符到
stdout
(文件描述符编号)的内容,要执行写入,您需要生成一个内核中断来执行。(对于x86,即
int80h
)。看看如何将一个字符写入
stdout

mov     eax, 4          ; linux (sys_write) in eax
mov     ebx, 1          ; fileno in ebx (stdout)
mov     ecx, achar      ; move achar address to ecx
mov     edx, 1          ; num chars to write in edx
int     0x80            ; kernel interrupt
(其中
achar
是要写入的字符的内存地址)

如注释中所述,您可以从数字
0-9
开始,并将
'0'
添加到值中以获得ASCII字符值(或者您可以简单地
使用
'0'
来完成相同的操作)。您也可以从ASCII值
'0'
开始,打印它,将其值增加
1
(以获得
'1'
,等等),然后总共执行10次。(于是我想到了一个循环)

我将让您进一步了解汇编中的循环,但基本方案是将循环计数(
10
)加载到
ecx
,然后跳转(
jmp
)(或
loop
)到循环的开始标签(例如
下一步:
looplbl:
)然后每次减小
ecx
中的值,直到您点击
0

循环完成后,要完成循环,请将程序的退出值加载到
ebx
中,并将
sys\u exit
加载到
eax
中,然后调用内核中断退出。现在,这个过程还有很多附加的子问题基础。这只是对解决方案的一种方法的概述。你只需要阅读和调查其余的内容,因为这远远超出了这篇文章的内容

要提供帮助,请执行以下示例。它只需从结束的ASCII值
'9'
中减去起始的ASCII值
'0'
(然后加上
1
,总共10个字符,并将该值用于循环计数)。然后循环10次,从打印
'0'
开始,并将
1
添加到每次通过循环打印的前一个值,直到打印完所有
'0'-'9'
。然后,它会打印一个
换行符
,这样数字就不会在提示和退出时出现在同一行:

section .data
    achar db '0'
    nwln db 0xa
section .text

        global _start               ; must be declared for using gcc
    _start:                         ; tell linker entry point
            xor     eax, eax        ; zero eax register
            mov     al, byte '9'    ; move byte '9` (57) to al
            sub     al, byte '0'    ; subtract byte '0' (48) from al
            inc     al              ; add 1 to al (for total chars)
            xor     ecx, ecx        ; zero exc
            mov     cx, ax          ; move result to cx (loop count)
    next:
            push    ecx             ; save value of ecx on stack
            mov     eax, 4          ; linux (sys_write) in eax
            mov     ebx, 1          ; fileno in ebx (stdout)
            mov     ecx, achar      ; move achar address to ecx
            mov     edx, 1          ; num chars to write in edx
            int     0x80            ; kernel interrupt

            pop     ecx             ; restore loop count
            mov     dx, [achar]     ; move value of achar to dx
            inc     byte [achar]    ; increment value of achar (next char)
            loop    next            ; jump to next:

            mov     eax, 4          ; linux (sys_write) in eax
            mov     ebx, 1          ; fileno in ebx (stdout)
            mov     ecx, nwln       ; move nwln (newline) to ecx
            mov     edx, 1          ; num chars to write in edx
            int     0x80            ; kernel interrupt

            xor     ebx, ebx        ; zero ebx (for exit code 0)
            mov     eax, 1          ; system call number (sys_exit)
            int 0x80                ; kernel interrupt
注意:您可以通过将上述代码中的
'9'
替换为
'9'
来输出ASCII字符集中的所有可打印字符)

编译/链接

这是一个nasm汇编程序的编译/链接示例。如果您使用的是fasm或yasm,则情况类似。我将所有对象文件写入
obj
subdir,将二进制文件写入
bin
subdir,只是为了减少混乱

nasm -f elf -o obj/prn_digits_32.o prn_digits_32.asm
ld -m elf_i386 -o bin/prn_digits_32 obj/prn_digits_32.o
输出

$ ./bin/prn_digits_32
0123456789

我希望这有帮助。如果你有问题,请告诉我。可能还有20种方法可以做到这一点,有些我相信会更好。但这实际上只是一步一个脚印的问题,并注意内存中每个寄存器和字节都在做什么。

汇编可能是一个需要交朋友的挑战。主要是因为你可以使用非常基本的构建块,
$ ./bin/prn_digits_32
0123456789