Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux NASM:如何使用英特尔64位汇编创建/处理基本bmp文件?_Linux_Assembly_X86_Nasm_X86 64 - Fatal编程技术网

Linux NASM:如何使用英特尔64位汇编创建/处理基本bmp文件?

Linux NASM:如何使用英特尔64位汇编创建/处理基本bmp文件?,linux,assembly,x86,nasm,x86-64,Linux,Assembly,X86,Nasm,X86 64,如何创建/处理简单的bmp文件,仅使用“英特尔64位汇编”和“nasm汇编”将其填充为一种颜色?包含此类操作的步骤包括: 创建具有固定值的bmp文件头(下面解释特定字段) 创建包含足够空间的缓冲区-每像素三个字节(一种颜色=红色+绿色+蓝色) 打开/创建文件 填满缓冲区 将头文件写入文件 将缓冲区写入文件 关闭文件 退出程序 广告2:这有点棘手-如果每行的像素数不能被4整除,程序必须用0xFF填充缺少的字节。在这里,我特意创建了一张201x201的图片。在这个例子中,我们可以看到每行有3*201

如何创建/处理简单的bmp文件,仅使用“英特尔64位汇编”和“nasm汇编”将其填充为一种颜色?

包含此类操作的步骤包括:

  • 创建具有固定值的bmp文件头(下面解释特定字段)
  • 创建包含足够空间的缓冲区-每像素三个字节(一种颜色=红色+绿色+蓝色)
  • 打开/创建文件
  • 填满缓冲区
  • 将头文件写入文件
  • 将缓冲区写入文件
  • 关闭文件
  • 退出程序
  • 广告2:这有点棘手-如果每行的像素数不能被4整除,程序必须用0xFF填充缺少的字节。在这里,我特意创建了一张201x201的图片。在这个例子中,我们可以看到每行有3*201=603个字节,这意味着每行需要额外的字节。因此,图片缓冲区所需的大小为604*201=121404

    回答问题的源代码:

    section     .text
    global      _start                              ;must be declared for linker (ld)
    
    _start:                                         ;tell linker entry point
    
    ;#######################################################################
    ;### This program creates empty bmp file - 64 bit version ##############
    ;#######################################################################
    ;### main ##############################################################
    ;#######################################################################
    
        ; open file
        mov     rax,85                              ;system call number - open/create file
        mov     rdi,msg                             ;file name
                                                    ;flags
        mov     rsi,111111111b                      ;mode
        syscall                                     ;call kernel
    
        ; save file descriptor
        mov     r8, rax
    
        ; write headline to file
        mov     rax, 1                              ;system call number - write
        mov     rdi, r8                             ;load file desc
        mov     rsi, bmpheadline                    ;load adress of buffer to write
        mov     rdx, 54                             ;load number of bytes
        syscall                                     ;call kernel
    
            mov         rbx, 201                    ;LOOPY counter
            mov         rdx, empty_space            ;load address of buffer (space allocated for picture pixels)
    LOOPY:
            mov         rcx, 201                    ;LOOPX counter
    
    LOOPX:
            mov         byte [rdx+0], 0x00          ;BLUE
            mov         byte [rdx+1], 0xFF          ;GREEN
            mov         byte [rdx+2], 0xFF          ;RED
    
            dec         rcx                         ;decrease counter_x
            add         rdx, 3                      ;move address pointer by 3 bytes (1 pixel = 3 bytes, which we just have written)
            cmp         rcx, 0                      ;check if counter is 0
            jne         LOOPX                       ;if not jump to LOOPX
    
            dec         rbx                         ;decrease counter_y
            mov         byte [rdx], 0xFF            ;additional byte per row
            inc         rdx                         ;increase address
            cmp         rbx, 0                      ;check if counter is 0
            jne         LOOPY                       ;if not jump to LOOPY
    
    
    
        ; write content to file
        mov     rax, 1                              ;system call number - write
        mov     rdi, r8                             ;load file desc
        mov     rsi, empty_space                    ;load adress of buffer to write
        mov     rdx, 121404                         ;load number of bytes
        syscall                                     ;call kernel
    
        ; close file
        mov     rax, 3                              ;system call number - close
        mov     rdi, r8                             ;load file desc
        syscall                                     ;call kernel
    
        ; exit program
        mov     rax,60                              ;system call number - exit
        syscall                                     ;call kernel
    
    section     .data
    
        msg         db  'filename.bmp',0x00         ;name of out file, 0x00 = end of string
        bmpheadline db  0x42,0x4D,0x72,0xDA,0x01,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x3C,0xDA,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
    
    section .bss                                    ;this section is responsible for preallocated block of memory of fixed size
    
        empty_space: resb 121404                    ;preallocation of 121404 bytes
    
    这里是bmp标题的解释(更多信息请点击此链接:)


    下面是rbraun答案的改进版本。这应该是codereview.SE>上的问答。这可能更适合。代码审阅:BMP标题格式描述内容不必分开。您可以在单独的行中写入
    db“BM”
    dd 54+201*(201+1)*3等,而不是
    db 0x42,0x4D,0x72,…
    。所有这些字节都是连续的,因为asm就是这样工作的。您甚至可以在标题的不同部分放置单独的标签。在前两个示例之后,您可以停止自己手动将代码编码为十六进制字节,让汇编程序从最具可读性的表达式中为您完成这项工作。e、 g.
    dd201
    而不是
    db 0xC9,0,0,0
    ,因为x86是小端数。
    mov-eax,85
    mov-rax,85
    效率更高(代码大小更小)。并非所有与NASM兼容的汇编程序都能为您优化这一点。对于所有其他对非64位对象进行操作的指令也是如此。(例如,移动文件描述符。无需64位mov。)
    msg
    是文件名标签的糟糕选择。称它为
    fname
    或其他什么。你的循环有点笨重。在内部循环的底部,使用
    addrdx,3
    /
    dec-ecx/jne-LOOPX
    。DEC和SUB根据结果设置ZF(就像几乎所有修改标志的指令一样),因此您不需要单独的CMP来实现倒计时循环。我投票结束这个问题,因为它是代码高尔夫。@RossRidge:我认为这更像是尝试编写一个示例(可能会继续)。很明显,OP并没有试图用问题的自我答案来解决任何问题。为了它的价值,我想通过使用单色位图(或RLE压缩,但它没有得到很好的支持)来改进代码。我还将使用NASM创建整个BMP文件,这样它就可以通过一个
    write
    系统调用或
    NASM-f bin-o filename.BMP
    @RossRidge:我考虑过将缓冲区放在数据段的头后(我猜你建议用
    TIMES db…
    填充它),但我认为这是一个更好的例子,说明如果这不是一个独立的程序,你可能会做什么。另外,我不想让二进制文件膨胀。不过,直接写出BMP的
    nasm-f bin
    是一个好主意。使用单色位图,您可以使图像数据完全为0,并且应该有足够的空间来安排事情,这样它实际上就不是可执行文件的一部分,而是零字段。@RossRidge:oh-hmm,使用某种链接器节/段技巧将初始化数据页放在BSS页的旁边?美好的是的,那完全行得通:)我根本没有使用BMP,只是稍微使用了一点,1bpp位图总是黑/白的,所以我没有意识到。
    ;### File Header - 14 bytes
    ;#######################################################################
    ;### bfType,        2 bytes,    The characters "BM"
    ;### 0x42,0x4D = "B","M"
    ;### 
    ;### bfSize,        4 bytes,    The size of the file in bytes
    ;### 0x72,0xDA,0x01,0x00 => 0x00,0x01,0xDA,0x72 = 0x1DA72 = 121458 bytes
    ;### 121458 = 54 + 201 * (201 + 1) * 3
    ;### 
    ;### Comment:
    ;### We want to create file 201x201, that means 201 rows and 201 columns
    ;### meaning each row will take 201*3 = 603 bytes
    ;### 
    ;### According to BMP file specification each such row must be adjusted
    ;### so its size is dividable by 4, this gives us plus 1 byte for each 
    ;### row.
    ;###
    ;###
    ;### bfReserved1,   2 bytes,    Unused - must be zero
    ;### 0x00,0x00
    ;### 
    ;### bfReserved2,   2 bytes,    Unused - must be zero
    ;### 0x00,0x00
    ;### 
    ;### bfOffBits,     4 bytes,    Offset to start of Pixel Data
    ;### 0x36,0x00,0x00,0x00 = 54 bytes
    ;### 
    
    ;### Image Header - 40 bytes
    ;#######################################################################
    ;### biSize             4   Header Size - Must be at least 40
    ;### 0x28,0x00,0x00,0x00 = 40
    ;### 
    ;### biWidth            4   Image width in pixels
    ;### 0xC9,0x00,0x00,0x00 = 201
    ;### 
    ;### biHeight           4   Image height in pixels
    ;### 0xC9,0x00,0x00,0x00 = 201
    ;### 
    ;### biPlanes           2   Must be 1
    ;### 0x01,0x00
    ;### 
    ;### biBitCount         2   Bits per pixel - 1, 4, 8, 16, 24, or 32
    ;### 0x18,0x00 = 24
    ;### 
    ;### biCompression      4   Compression type (0 = uncompressed)
    ;### 0x00,0x00,0x00,0x00
    ;### 
    ;### biSizeImage        4   Image Size - may be zero for uncompressed images
    ;### 0x3C,0xDA,0x01,0x00 => 0x00,0x01,0xDA,0x3C = 121404 bytes
    ;### 
    ;### biXPelsPerMeter    4   Preferred resolution in pixels per meter
    ;### 0x00,0x00,0x00,0x00
    ;### 
    ;### biYPelsPerMeter    4   Preferred resolution in pixels per meter
    ;### 0x00,0x00,0x00,0x00
    ;### 
    ;### biClrUsed          4   Number Color Map entries that are actually used
    ;### 0x00,0x00,0x00,0x00
    ;### 
    ;### biClrImportant     4   Number of significant colors
    ;### 0x00,0x00,0x00,0x00
    ;### 
    
    DEFAULT REL            ; default to RIP-relative addressing for static data
    
    ;#######################################################################
    ;### This program creates empty bmp file - 64 bit version ##############
    
    section     .rodata                  ; read-only data is the right place for these, not .data
    
    
        BMPcols   equ  2019
        BMProws   equ  2011
    
        ; 3 bytes per pixel, with each row padded to a multiple of 4B
        BMPpixbytes equ 3 * BMProws * ((BMPcols + 3) & ~0x3)
    
        ;; TODO: rewrite this header with separate db and dd directives for the different fields.  Preferably in terms of assembler-constant width and height
    
        ALIGN 16   ; for efficient rep movs
    bmpheader:
    ;; BMP is a little-endian format, so we can use dd and stuff directly instead of encoding the bytes ourselves
    bfType:  dw "BM"
    bfSize:  dd BMPpixbytes + bmpheader_len   ; size of file in bytes
             dd 0                 ; reserved
    bfOffBits: dd bmpheader_len   ; yes we can refer to stuff that's defined later.
    
    biSize:   dd 40    ; header size, min = 40
    biWidth:  dd BMPcols
    biHeight: dd BMProws
    biPlanes:       dw 1     ; must be 1
    biBitCount:     dw 24    ; bits per pixel: 1, 4, 8, 16, 24, or 32
    biCompression:  dd 0     ; uncompressed = 0
    biSizeImage:    dd BMPpixbytes  ; Image Size - may be zero for uncompressed images
    
    biXPelsPerMeter: dd 0   ;  Preferred resolution in pixels per meter
    biYPelsPerMeter: dd 0   ;  Preferred resolution in pixels per meter
    biClrUsed:       dd 0   ;  Number Color Map entries that are actually used
    biClrImportant:  dd 0   ;  Number of significant colors
    
        bmpheader_len   equ   $ - bmpheader         ; Let the assembler calculate this for us.  Should be 54.  `.` is the current position
    
        ; output filename is hard-coded.  Checking argc / argv is left as an exercise for the reader.
        ; Of course it would be even easier to be more Unixy and just always write to stdout, so the user could redirect
        fname         db  'filename.bmp',0x00         ;name of out file, 0x00 = end of string
    
    
    section .bss                                    ;this section is responsible for fixed size preallocated blocks
    
        bmpbuf: resb 54 + BMPpixbytes    ; static buffer big enough to hold the whole file (including header).
        bmpbuf_len  equ  $ - bmpbuf
    
    
    section     .text
    global      _start                              ;make the symbol externally visible
    
    _start:                                         ;The linker looks for this symbol to set the entry point
    
    ;#######################################################################
    ;### main ##############################################################
    
        ; creat(fname, 0666)
        mov     eax,85                              ; SYS_creat from /usr/include/x86_64-linux-gnu/asm/unistd_64.h
        ;mov     edi, fname                          ;file name string.  Static data is always in the low 2G, so you can use 32bit immediates.
        lea     rdi, [fname]                        ; file name, PIC version.  We don't need [rel fname] since we used DEFAULT REL.
                                                    ; Ubuntu 16.10 defaults to enabling position-independent executables that can use ASLR, but doesn't require it the way OS X does.)
                                                    ;creat doesn't take flags.  It's equivalent to open(path, O_CREAT|O_WRONLY|O_TRUNC, mode).
        mov     esi, 666o                          ;mode in octal, to be masked by the user's umask
        syscall                              ; eax = fd or -ERRNO
    
        test  eax,eax             ; error checking on system calls.
        js    .handle_error       ; We don't print anything, so run under strace to see what happened.
    
    
        ;;; memcpy the BMP header to the start of our buffer.
        ;;; SSE loads/stores would probably be more efficient for such a small copy
        mov    edi, bmpbuf
        mov    esi, bmpheader
         ;Alternative: rep movsd or movsq may be faster.
         ;mov    ecx, bmpheader_len/4 + 1   ; It's not a multiple of 4, but copy extra bytes because MOVSD is faster
        mov    ecx, bmpheader_len
        rep movsb
    
        ; edi now points to the first byte after the header, where pixels should be stored
        ; mov  edi, bmpbuffer+bmpheader_len might let out-of-order execution get started on the rest while rep movsb was still running, but IDK.
    
    
    ;#########  main loop
            mov         ebx, BMProws
    .LOOPY:                                     ; do{
    
            mov         ecx, BMPcols  ; Note the use of a macro later to decide whether we need padding at the end of each row or not, so arbitrary widths should work.
    
    .LOOPX:                                        ; do{
            mov         dword [rdi],  (0xFF <<16) | (0xFF <<8) | 0x00      ;RED=FF, GREEN=FF, BLUE=00
                                                    ; stores one extra byte, but we overlap it with the next store
    
            add         rdi, 3                      ;move address pointer by 3 bytes (1 pixel = 3 bytes, which we just have written)
            dec         ecx
            jne         .LOOPX                     ; } while(--x != 0)
        ; end of inner loop
    
    
    %if    ((BMPcols * 3) % 4) != 0
            ; Pad the row to a multiple of 4B
            mov         dword [rdi], 0xFFFFFFFF    ; might only need a byte or word store, but another dword store that we overlap is fine as long as it doesn't go off the end of the buffer
    
            add         rdi, 4 - (BMPcols * 3) % 4  ; advance to a 4B boundary
    %endif
    
            dec         ebx
            jne         .LOOPY                    ; } while(--y != 0)
    
    
    ;##### Write out the buffer to the file
    
        ; fd is still where we left it in RAX.
        ; write and close calls both take it as the first arg,
        ;  and the SYSCALL ABI only clobbers RAX, RCX, and R11, so we can just put it in EDI once.
        mov     edi, eax                            ; fd
    
        ; write content to file: write(fd, bmpbuf, bmpbuf_len)
        mov     eax, 1                              ;SYS_write
        lea     rsi, [bmpbuf]                       ;buffer.
        ; We already have enough info in registers that reloading this stuff as immediate constants isn't necessary, but it's much more readable and probably at least as efficient anyway.
        mov     edx, bmpbuf_len
        syscall
    
        ; close file
        mov     eax, 3                              ;SYS_close
        ; fd is still in edi
        syscall
    
    .handle_error:
        ; exit program
        mov     rax,60                              ;system call number - exit
        syscall