1

这是我正在做的一个粗略的、wip 的爱好项目,只是为了在 dos 中打印简单的 16 色 BMP。在对此进行修补时,我遇到了一个意外错误,我一直无法找到有关该错误的信息。

每当将line_len 移动到 dx的行被注释掉,或者当 dx 被 ax 替换时,相关错误就会消失。

new_line: 
    mov dx,[line_len]       ;;restock on pixels
    sub cx,[line_pad_len]   ;;decrement cx by the padlen -1 to skip the padding.
    add bx,[line_pad_len]   ;;increment read address.
    inc bx 
    loop process_loop       ;;return to loop and decrement.

读取的错误(尽管确切的数字会发生变化,我认为这是自然的)

Invalid Opcode at EEAf 2D00 0217 0000 [... rest is zeroes ]
Invalid Opcode at 0013 0000 0202 0000 0013 0100 0002 0001 756E 6573 0864 0607 0405
Invalid Opcode at FECB 118A 0202 118A 189C 0000 4D4E 0000 [...]

并随后停止 FreeDOS。当对此运行调试并逐步执行时,我能读到的最后一个是(第一个)LOOP 助记符,通常它会跳到循环中的第一个更改。(据我所知。)

不幸的是,我是 DOS 和 Assembly 的新手,并且无法使用此信息找到解决方案。如果我用 AX 替换 DX,错误就会消失,但我宁愿尝试了解为什么会出现此错误,以便将来避免它。

下面的 bmp->bin 转换器的完整源代码。

org 100h

segment .code
    mov ax,3d00h            ;;OPEN FILE WITH 00 ACCESS. (READ ONLY)
    mov dx,filename
    int 21h
    jc exit                 ;;C FLAG MEANS ERROR.
    mov bx,ax               ;;GET FILE HANDLE

    mov ax,3f00h            ;;read file.
    mov cx,400h             ;;1024byte buffer available.
    mov dx,file_buffer      ;;address to the buffer.
    int 21h
    jc exit                 ;;C etc.
    mov cx,ax               ;;Move read bytes into cx.

    mov ax,3e00h            ;;close file
    int 21h
    jc exit

;;confirm_file:
    cmp word [file_buffer],4d42h
    jnz exit_bmp            ;;THIS IS NOT A BMP FILE.

    mov dx,00h
    mov ax, word [file_buffer+0022h]
    div word [file_buffer+0016h]
    dec ax
    mov [line_pad_len],ax

    mov dx, word [file_buffer+0012h]    ;;get width of image in pixels.
    cmp dx,0050h            ;;check if it's too wide for our screen.
;;jmp if it is.
    mov [line_len],dx
    mov bx, word [file_buffer+000ah]    ;;get offset of bmp array.
    mov cx, word [file_buffer+0022h]    ;;get size of pixel array + padding

process_loop:               ;;WE WANT 16 COLOUR BMP. 2PX/BYTE. LEFTMOST PX MOST SIGNIFICANT NIBBLE.
    mov al,[file_buffer+bx] ;;GET FIRST BYTE OF PIXEL ARRAY.
    inc bx                  ;;INCREMET OUR FILE READ LOCATION.
    dec dx
    jbe new_line            ;;if we are out of line, skip back.
    mov ah,al               ;;copy al into ah for safekeeping
    shr ah,04h              ;;ah shifted left 4bit. high nibble should be 0
    and al,0fh              ;;high nibble zeroed.
                            ;;WRITE NEW DATA TO BUFFER. (STILL UPSIDE DOWN)
    mov [outp_buffer+di],ah ;;write ax
    inc di
    mov [outp_buffer+di],al ;;write al
    inc di
    loop process_loop       ;;DECREMENT CX LOOP
    jmp write_file

new_line: 
    mov dx,[line_len]       ;;restock on pixels
    sub cx,[line_pad_len]   ;;decrement cx by the padlen -1 to skip the padding.
    add bx,[line_pad_len]   ;;increment read address.
    inc bx 
    loop process_loop       ;;return to loop and decrement.

write_file:                 ;;WARNING! THIS WILL DESTROY THE FILE IT WRITES TO.
    mov ah,3ch              ;;CREAT FILE
    mov dx,newfile          ;;PTR TO FILENAME
    mov cx,0000h            ;;FLAGS
    int 21h
    jc exit
    mov bx,ax               ;;file handle.

    mov ah,40h              ;;write to our file
    mov cx,di               ;;di should have bytes written.
    mov dx,outp_buffer      ;;get pointer to output buffer.
    int 21h
    jc exit                 ;;did we fail?

    mov ah,3eh              ;;Close our file.
    int 21h

exit:
    mov ah,4ch
    int 21h

exit_bmp:
    mov ax,4c66h
    int 21h

segment .data
filename:       db  "IN.BMP",00h
line_len:       dw  0000h
newfile:        db  "OUT.BIN",00h
line_pad_len:   dw  0000h
segment .bss
file_buffer:    resb    1024    ;;FIGURE OUT BETTER WAY TO DO LEN.
outp_buffer:    resb    1024    ;;FIGURE OUT BETTER STUFF.
4

1 回答 1

2

我在您的代码中发现了一些问题:

JBE之后不要使用DECJBE跳转 if CF=1 or ZF=1,但DEC不修改进位标志。但是,CMP DX,50h上面确实修改了进位标志,因此根据DX( line_len) 的值,您可能会在第一次迭代时得到不正确的跳转。如果你想使用JBE你应该使用SUB DX,1而不是DEC DX,因为SUB修改了进位标志。


CX对循环内部的更新不正确。
考虑一个 80*32 像素的图像:像素数组的大小将是 80*32/2 == 0x500 字节。你的line_len将是 0x50,你的line_pad_len将是 0x500 / 0x20 - 1 == 0x27。第process_loop一个扫描线运行 80 次,所以当你第一次CX到达时将是 0x4B0 。new_line然后你又减少CX ;这次是 0x27+1。因此,CX每条扫描线总共减少 80+0x27+1 == 0x78。由于 0x500 不能被 0x78 整除,因此CX会环绕而不是达到零,从而创建一个无限(或至少太长)循环。


如上所述,您的内部循环line_len在每个扫描线迭代次数(即每个像素一次迭代),并且每次迭代都将文件缓冲区索引 ( BX) 更新为 1。但是缓冲区只包含像素数的一半。即使您已经在内部循环中增加了,您也会在每个扫描线的末尾添加line_pad_lento 。唯一应该添加到这里的是填充字节的数量 (如果有的话)。BXBXBX

于 2013-10-02T16:16:33.553 回答