assembly 为什么x86汇编程序会绘制错误的符号

xytpbqjk  于 2023-03-18  发布在  其他
关注(0)|答案(1)|浏览(101)

我是一个大学生,我不知道我的错误在哪里。这是我的作业,用x86汇编语言编写。(我用INTEL语法编写,我用DosBOX运行它)
用任何文本填充显存的第0页。在屏幕的第一行,显示16个NUL字符(ASCII代码0),背景属性的值不同(符号属性的最高四元组)。在第一行按下左键以选择并存储所选颜色。当光标放置在屏幕其余行的任何字符上时,字符的颜色将通过按向左按钮变为所选的颜色。
但是程序改变了一个符号的颜色,而不是我用光标点击的那个。

.386
Data segment use16
    ColorOne db 2fh
    ColorMain db 0fh
    sym db 100
    style db ?
Data ends

Code segment use16
assume cs:Code, ds:Data
start:
    mov ax, DATA
    mov ds, ax
    mov ax, 0B800h
    mov es, ax

    mov ax, 3           ; 80x35
    int 10h 

    mov bx, 0
    xor di, di

    ; screen fill
loop1:
    add di, bx
    imul di, 2
    mov ah, ColorOne        
    mov al, 0

    stosw

    add ColorOne, 10h
    inc bx
    xor di, di
    cmp bx, 16
    jl loop1

    mov bx, 1

loop2:
    mov cx, 0
    loop3:
        add di, bx
        imul di, 80
        add di, cx
        imul di, 2
        mov ah, ColorMain       
        mov al, sym

        stosw

        inc cx
        xor di, di
        cmp cx, 80
        jl loop3
        
    inc bx
    inc sym
    cmp bx, 25
    jl loop2
;---------------------------------------------------------
    mov ax, 1           ; show cursor
    int 33h

    mov ax, 0ch
    mov cx, 11b         ; move and press left button

    push es     ; save segment status
    push cs
    pop  es

    lea dx, prmaus      ; set the offset of the event processing procedure from the mouse in the code segment

    int 33h         ; registration of the address and conditions of the call
    pop es

    mov ah, 01h     ; wait
    int 21h     ; pause

    xor cx,cx           ; disconect mouse
    mov ax,0ch      
    int 33h

    mov ax, 3           ; clean screen
    int 10h

    mov ax, 4c00h   ; exit
    int 21h 
;----------------------------------------------------------
prmaus proc far
    ; saving the contents of registers ds, es
    push ds
    push es
    pusha
    push 0b800h
    pop es  
    push Data
    pop ds

    ; algorithm
to_left_mouse:
    cmp bx, 1b
    jne to_end

    mov ax, 2               ; hide cursor
    int 33h

    first_row:
        cmp dx, 0           ; If DX is equal to 0, the mouse is over the first row
        jne another_row     ; If the mouse is not over the first row, jump to processing other rows
        shr cx, 2           ; Shift CX right by 2 bits (divide by 4, because each character takes up 4 bytes)
        mov di, cx          ; Store the value of CX in DI (for array indexing)
        mov bx, es:[di]     ; Get the value of the character color in the row
         shr bx, 8          ; Shift the color value right by 8 bits
        mov bh, bl          ; Copy the lower byte to the upper byte of the BH register (to preserve the color)
         mov style, bh      ; Store the color in the style variable

        jmp to_end          ; Jump to the end of the procedure

another_row:                ; Label for processing other rows of the window
        xor di, di          ; Zero out the index of the screen buffer

        add di, dx          ; Add the value of DX to DI (to get the row number)
        imul di, 20         ; Multiply the value of DI by 20 (the width of the window in characters)
        shr cx, 2           ; Shift CX right by 2 bits (divide by 4, because each character takes up 4 bytes)
        add di, cx          ; Add the value of CX to DI (to get the column number)

        mov ax, es:[di]     ; Get the value of the character from the screen buffer
        mov al, ah          ; Copy the upper byte to the lower byte (to preserve the character)

        mov ah, style       ; Store the color value in the AH register

        stosw               ; Save the value in the screen buffer

to_end:

    mov ax, 1           ; show cursor
    int 33h

    popa
    pop es
    pop ds
    retf
prmaus  endp
Code ends
end start

我猜想问题可能是两个字节的移位(但另一方面,我尝试了不同的变体,无法得到正确的结果)
shr cx,2 ;将CX右移2位(除以4,因为每个字符占用4个字节)
预计当你点击光标上的颜色时会被选中,而点击符号后,它会变成选中的颜色

mccptt67

mccptt671#

shr cx, 2           ; Shift CX right by 2 bits (divide by 4, because each character takes up 4 bytes)

评论中的推理是错误的!移动两次只是算术上的捷径。
因为鼠标驱动程序给出CX = 8 * 列,所以我们需要除以8,因为对于显存中的每个字符单元格,我们使用一个单词,所以我们需要乘以2。组合意味着我们需要除以4,而向右双移可以最有效地完成这一操作。

imul di, 20         ; Multiply the value of DI by 20 (the width of the window in characters)

评论中的推理是错误的!乘以20只是一种算术捷径。
因为鼠标驱动程序给出DX = 8 * Row,所以我们需要除以8,而且因为显存中每行字符使用160字节,所以我们需要乘以160。组合意味着我们需要乘以20。

mov bh, bl          ; Copy the lower byte to the upper byte of the BH register (to preserve the color)
mov al, ah          ; Copy the upper byte to the lower byte (to preserve the character)

这些操作没有保留任何东西!在后一种情况下,这是为什么“程序改变了一个符号的颜色,而不是我用光标点击的符号”的根本问题。

这些是修复程序

shr  cx, 2
    imul di, dx, 20
    add  di, cx
    test dx, dx
    jnz  another_row
first_row:
    mov  al, es:[di+1]  ; Get the value of the attribute
    mov  style, al      ; Store the color in the style variable
    jmp  to_end         ; Jump to the end of the procedure
another_row:
    mov  al, style
    mov  es:[di+1], al  ; Change the value of the attribute
to_end:

使用es:[di+1]可以直接寻址属性字节。
imul di, dx, 20(最好的x86指令之一)执行DI = DX * 20。

相关问题