装配项目,在Intel i386架构上使用YASM向文本添加行号

时间:2017-11-21 19:40:22

标签: linux assembly text x86

我必须在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

1 个答案:

答案 0 :(得分:0)

如果您只是直接使用读/写系统调用,一个明显的算法是分配第二个缓冲区(大到足以保存结果,即使输入的每个字节都是换行符)。你从未接触的页面并不真正算作使用,因此拥有一个非常大的BSS是好的。

循环:

  • do {
  • 将当前行号计数器格式化为字符串,将其格式化为输出缓冲区的当前位置,以及尾随空格或制表符。 (NASM / YASM示例How do I print an integer in Assembly Level Programming without printf from the c library?很容易从x86-64移植到i386。)但是每次避免重新执行div - 10个内容会更有效,并且只需增加最低有效数字,直到它为> '9',然后重做格式化。)
  • 从输入缓冲区复制字节直到包括下一个换行符('\n' = 0xa。YASM不支持NASM的处理C样式转义序列的反引号字符串/字符文字。如果不以换行符结束,也要确保停在缓冲区的末尾。 (您可以在循环之前检查这个,如果没有循环则附加一个,这样可以简化循环)。
  • } while(input_pos < end_of_input)

完成后,通过从缓冲区的开头减去当前位置来查找结果的长度,并将其用于sys_write

如果您想支持大于输入/输出缓冲区的文件,请记住在循环返回另一行sys_read时是否在一行的末尾。 (而不是实际将该部分行复制回输入缓冲区的开头。复制策略将失败,其行长于缓冲区大小。)

不要忘记sys_readsys_write可以提前返回,读取或写入的字节数比您要求的少,即使有更多的字节需要读/写。 (例如,如果您正在读取管道,包括命名管道,或者在系统调用期间有信号进入)。 IIRC,libc包装器函数可能会处理重试。

另一种方法可能是扫描计数换行符,然后从缓冲区的末尾开始工作以将其扩展到原位。这样可以稍微提高缓存效率,但它有一个缺点,即需要初始扫描来计算换行数,以确定需要留出多少空间(以及从中倒数的行号)。

我的第一个建议的优点是,您只需触摸一次输入字符,而不是两次输入字符。如果你使用一个适合L1D缓存的小型缓冲区(如16kiB),那么从头到尾扩展它可能是值得考虑的,但它更复杂。

OTOH,如果你真的想要优化效率,你可以使用vmsplice(2)将一些页面“赠送”到内核,进入一个临时管道并从那里splice将它们变成一个常规文件。因此,您编写的物理页面最终将作为pagecache的一部分进行零拷贝。但是,这可能比仅仅进行write()系统调用有更多的开销。

以上两种方法都具有仅对整个缓冲区进行一次大write()系统调用的优点。也有可能制作一个低效的程序,将行号+一行复制到tmp缓冲区并在其上使用sys_write,甚至更差sys_write行号文本,然后{{1}输入缓冲区中的就地行。

IDK,如果这更容易实现,因为你仍然必须得到正确的所有字节数,并且它的性能很糟糕。与复制几个字节相比,sys_write相当慢。