这是一个粗略的,我正在做的爱好项目,只是为了在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.
答案 0 :(得分:2)
我在你的代码中发现了一些问题:
请勿在{{1}}之后使用JBE
。 DEC
会JBE
跳转,但CF=1 or ZF=1
不会修改进位标记。然而,上面的DEC
会修改进位标记,因此您可能会在第一次迭代时获得不正确的跳转,具体取决于CMP DX,50h
(DX
)的值。如果您想使用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
的是填充字节数(如果有的话)。