我必须在i386架构(32位)上使用YASM创建一个汇编程序,它接收一个文本作为参数,并返回一个文本相同的文本,但每行都有编号。
示例:
00这是最终文本应该是什么样的
01所有编号为
的行02,最后一行应该有总行数
03总计:3。
; $ yasm -f elf enum.asm
; $ ld -o enum enum.o
; $ ./fibonacci
%define stdout 1
section .data
file_name db 'test.txt'
new_file db 'resultado.txt'
num db "00: ",4,
numL equ $ - num
bufferEntradaL dd 1
salto db 0xa
section .bss
descriptorEntrada resb 2
bufferEntrada resb 2
descriptorSalida resb 2
descriptorEntrada resb 2
punteroBuffer resb 2
cant resb 2
section .text
global _start
abrirArchivoLectura:
;Abre un archivo
mov EAX, sys_open ; Llamo a sys_open
mov ECX, 0 ; Solo lectura
mov EBX file_name ; Nombre del archivo
int 80h ; Llamo al sistema
ret
abrirArchivoEscritura:
mov EAX, sys_open ; Llamo al sys_open
mov ECX, 1 ; Modo solo escritura
mov EBX new_file ; Nombre del archivo
int 80h ; Llamo al sistema
ret
crearArchivoEscritura:
mov EAX, sys_create
mov EBX new_file
mov ECX, 1
int 80h
ret
leerArchivo:
;Lee un archivo
mov EAX, sys_read ; Llamo a sys_read
mov EBX, [descriptorEntrada] ; Descriptor del archivo
mov ECX, bufferEntrada ; Buffer de entrada
mov EDX, bufferEntradaL ; Tamaño del buffer
int 80h ; Llamo al sistema
ret
imprimirMensaje:
;Imprime un mensaje de ayuda
mov EAX, sys_write ; Llamo a sys_write
mov EBX, stdout ; Imprimir por pantalla
mov ECX, num ; Mensaje a imprimir
mov EDX, numL ; Longitud
int 0x80 ; Llamo al sistema
jmp salirSinError ; Sale sin error
imprimirSaltoPantalla:
;Imprime un salto de linea por pantalla
mov EAX, sys_write ; Llamo a sys_write
mov EBX, stdout ; Imprimir por pantalla
mov ECX, salto ; Mensaje a imprimir
mov EDX, 1 ; Longitud
int 0x80 ; Llamo al sistema
ret
cerrarArchivoEntrada:
;Cierra el archivo de entrada
mov EAX, sys_close ; Llamo a sys_close
mov EBX, [descriptorEntrada] ; Muevo el descriptor de salida al registro EBX
int 80h ; Llamo al sistema
ret
cerrarArchivoSalida:
;Cierra el archivo de salida
mov EAX, sys_close ; Llamo a sys_close
mov EBX, [descriptorSalida] ; Muevo el descriptor de salida al registro EBX
int 80h ; Llamo al sistema
ret
leerHastaSaltoLinea:
mov [punteroBuffer],ECX ; Le asigna a la variable punteroBuffer el contenido del registro ECX
mov [cant],EAX ; Le asigna a la variable cant el contenido del registro EAX
cmp cant,salto
jne leerHastaSaltoLinea
loop:
_start:
;Comienza el programa
call
call abrirArchivoLectura ; Abre el archivo de entrada
test EAX,EAX ; Testea que el parametro ingresado por el usuario sea un archivo.txt
js salirErrorArchivoEntrada ; Si no es un archivo.txt sale con un error de archivo de entrada
mov [descriptorEntrada],EAX ; Guardo el descriptor del archivo de entrada
call leerArchivo ; Lee el archivo de salida
call leerHastaSaltoLinea
salirErrorArchivoEntrada:
;Salir con error en archivo de entrada
mov EAX,sys_exit ; Llamo a sys_exit
mov EBX, 2 ; Finalizo por error en el archivo de entrada
int 0x80 ; Llamo al sistema
答案 0 :(得分:0)
如果您只是直接使用读/写系统调用,一个明显的算法是分配第二个缓冲区(大到足以保存结果,即使输入的每个字节都是换行符)。你从未接触的页面并不真正算作使用,因此拥有一个非常大的BSS是好的。
循环:
do {
div
- 10个内容会更有效,并且只需增加最低有效数字,直到它为> '9'
,然后重做格式化。)'\n'
= 0xa
。YASM不支持NASM的处理C样式转义序列的反引号字符串/字符文字。如果不以换行符结束,也要确保停在缓冲区的末尾。 (您可以在循环之前检查这个,如果没有循环则附加一个,这样可以简化循环)。} while(input_pos < end_of_input)
完成后,通过从缓冲区的开头减去当前位置来查找结果的长度,并将其用于sys_write
。
如果您想支持大于输入/输出缓冲区的文件,请记住在循环返回另一行sys_read
时是否在一行的末尾。 (而不是实际将该部分行复制回输入缓冲区的开头。复制策略将失败,其行长于缓冲区大小。)
不要忘记sys_read
和sys_write
可以提前返回,读取或写入的字节数比您要求的少,即使有更多的字节需要读/写。 (例如,如果您正在读取管道,包括命名管道,或者在系统调用期间有信号进入)。 IIRC,libc包装器函数可能会处理重试。
另一种方法可能是扫描计数换行符,然后从缓冲区的末尾开始工作以将其扩展到原位。这样可以稍微提高缓存效率,但它有一个缺点,即需要初始扫描来计算换行数,以确定需要留出多少空间(以及从中倒数的行号)。
我的第一个建议的优点是,您只需触摸一次输入字符,而不是两次输入字符。如果你使用一个适合L1D缓存的小型缓冲区(如16kiB),那么从头到尾扩展它可能是值得考虑的,但它更复杂。
OTOH,如果你真的想要优化效率,你可以使用vmsplice(2)
将一些页面“赠送”到内核,进入一个临时管道并从那里splice
将它们变成一个常规文件。因此,您编写的物理页面最终将作为pagecache的一部分进行零拷贝。但是,这可能比仅仅进行write()
系统调用有更多的开销。
以上两种方法都具有仅对整个缓冲区进行一次大write()
系统调用的优点。也有可能制作一个低效的程序,将行号+一行复制到tmp缓冲区并在其上使用sys_write
,甚至更差sys_write
行号文本,然后{{1}输入缓冲区中的就地行。
IDK,如果这更容易实现,因为你仍然必须得到正确的所有字节数,并且它的性能很糟糕。与复制几个字节相比,sys_write
相当慢。