gpt4 book ai didi

assembly - 在 linux 控制台中显示所有 ascii 字符(NASM 程序集)

转载 作者:行者123 更新时间:2023-12-02 20:05:00 28 4
gpt4 key购买 nike

我阅读了关于 nasm 的教程,有一个代码示例显示了整个 ascii 字符集。除了为什么我们要推送 ecx 和弹出 ecx 之外,我几乎了解所有内容,因为我看不到它与其余代码的关系。 Ecx 的值为 256,因为我们想要所有字符,但不知道在哪里以及如何使用它。当我们 push 和 pop ecx 时到底发生了什么?为什么我们要把achar的地址移到dx?我没有看到我们使用 dx 做任何事情。我知道我们需要增加 achar 的地址,但我很困惑增量与 ecx 和 dx 的关系。我会很感激一些见解。

   section  .text
global _start ;must be declared for using gcc

_start: ;tell linker entry point
call display
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel

display:
mov ecx, 256

next:
push ecx
mov eax, 4
mov ebx, 1
mov ecx, achar
mov edx, 1
int 80h

pop ecx
mov dx, [achar]
cmp byte [achar], 0dh
inc byte [achar]
loop next
ret

section .data
achar db '0'

最佳答案

I understand pretty much everything



好吧,那么您有点领先于我......(尽管从您的进一步评论中您会意识到该代码中的其他一些无意义的事情:))。

why are we pushing ecx and popping ecx as I dont see how it relates to the rest of the code. Ecx has the value of 256 since we want all chars but no idea where and hows its used.



它被 LOOP 指令使用(这不是一个好主意: Why is the loop instruction slow? ),它将递减 ecx ,并在 value 大于零时跳转,即它是一个倒计时循环机制。

由于 int 0x80 服务调用需要 ecx 作为内存地址值,因此计数器由 push/ pop 保存/恢复。一种更高效的方法是将计数器值放入一些备用寄存器中,例如 esi ,然后执行 dec esi jnz next 。更高效的方法是重新使用字符值本身,如果输出将从零值开始,而不是零数字,那么 inc byte [achar] 之后的零标志可用于检测循环条件。
achar db '0'

我不清楚,为什么“显示所有 ASCII 字符”从数字零(值 48 )开始,对我来说似乎很奇怪,我会从零开始。但这还有一个警告,linux 控制台 I/O 编码是由环境设置的,现在在任何常见的 linux 安装上都是 UTF8,因此有效的可打印单字节字符的值只有 32-126(与普通的 7位 ASCII 编码,使这部分示例工作良好),值 0-31 和 127 是不可打印的控制字符,也与常见的 7b ASCII 编码相同。值 128-255 表示 UTF8 编码的多字节字符(例如: ř 是两个字节 0xC5 0x99 ),作为单个字节,它们是无效的字节序列,因为缺少 UTF8“代码点”字节的剩余部分。

在 DOS 时代,您可以直接将代码写入 VGA 文本模式视频内存,从 0 到 255 的全 8 位值,每个值都有 distinct graphical representation ,您可以在 VGA 自定义字体或已知代码页中指定特定字符,这有时也被称为“扩展 ASCII”,但常见的 DOS 安装与您评论中的链接不同,有更多的方框图字符。这包括 \r\n 控制字符,它们对于 VGA 来说只是另一种字体字形,而不是换行符和换行符控制字符(该含义是由 BIOS/DOS 服务调用创建的,而不是输出 \n 字符将移动内部光标到下一行并丢弃输出中的字符)。

不可能使用 linux 控制台 I/O 重新创建它(除非 UTF8 字体包含所有奇怪的 DOS 字形,并且您将输出它们正确的 UTF8 编码而不是单字节值)。

结论是,该示例以值 '0' ( 48 ) 开始,直到值 126 它输出正确的可打印 ASCII 字符,在 126 之后它输出“某物”,并且由于这些字节有时会形成无效的 UTF8 编码,我会在技术上称之为具有未定义行为的“虚假”输出,对于不同的 linux 版本和控制台设置,您可能会得到不同的结果。

还有 NASM 风格的注意事项:在标签后放置冒号,即 achar: db '0' ,当您不小心使用指令助记符作为标签时,如 loop:dec: db 'd' ,这将节省您的时间。
   mov  dx, [achar]
dx 不再使用,因此这是无用的指令。
   cmp  byte [achar], 0dh

此比较中的标志也不再使用,因此这也是无用的。

所以调整后的例子看起来像这样:

section  .text
global _start ;must be declared for using gcc

_start: ;tell linker entry point
call display
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel

; displays all valid printable ASCII characters (32-126), and new-line after.
display:
mov byte [achar], ' ' ; first valid printable ASCII
next:
mov eax, 4
mov ebx, 1
mov ecx, achar
mov edx, 1
int 0x80
inc byte [achar]
cmp byte [achar], 126
jbe next ; repeat until all chars are printed
; that will output all 32..126 printable ASCII characters

; display one more character, new line (reuse of registers)
mov byte [achar], `\n` ; NASM uses backticks for C-like meta chars
mov eax, 4 ; ebx, ecx and edx are already set from loop above
int 0x80
ret

section .bss
achar: resb 1 ; reserve one byte for character output

但是首先在内存中准备整个输出,然后一次性输出它会更有意义,如下所示:

section  .text
global _start ;makes symbol "_start" global (visible for linker)

_start: ;linker's default entry point
call display
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel

; displays all valid printable ASCII characters (32-126), and new-line after.
display:
; prepare in memory string with all ASCII chars and new-line
mov al,' ' ; first valid printable ASCII
mov edi, allAsciiChars
mov ecx, edi ; this address will be used also for "write" int 0x80
nextChar:
mov [edi], al
inc edi
inc al
cmp al, 126
jbe nextChar
; add one more new line at end
mov byte [edi], `\n`
; display the prepared "string" in one "write" call
mov eax, 4 ; sys_write, ecx is already set
mov ebx, 1 ; file descriptor STDOUT
lea edx, [edi+1]; edx = edi+1 (memory address beyond last char)
sub edx, ecx ; edx = length of generated string
int 0x80
ret

section .bss
allAsciiChars: resb 126-' '+1+1 ; reserve space for ASCII characters and \n

所有示例都在 64b linux(基于 Ubuntu 16.04 的“KDE neon”发行版)上使用 nasm 2.11.08 进行了尝试,并通过命令构建:
nasm -f elf32 -F dwarf -g test.asm -l test.lst -w+all
ld -m elf_i386 -o test test.o

带输出:
$ ./test
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

关于assembly - 在 linux 控制台中显示所有 ascii 字符(NASM 程序集),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48046707/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com