将存储的值移动到DX会导致DOS中的无效操作码

时间:2013-10-01 23:52:02

标签: assembly x86 dos nasm

这是一个粗略的,我正在做的爱好项目,只是为了在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和汇编的新手,并且无法找到这个信息的解决方案。如果我用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.

1 个答案:

答案 0 :(得分:2)

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

请勿在{{1​​}}之后使用JBEDECJBE跳转,但CF=1 or ZF=1不会修改进位标记。然而,上面的DEC 会修改进位标记,因此您可能会在第一次迭代时获得不正确的跳转,具体取决于CMP DX,50hDX)的值。如果您想使用line_len,则应使用JBE代替SUB DX,1,因为DEC DX会修改进位标记。


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


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