I'm a university student and I can't figure out where my mistake is. Here's my assignment to write in x86 assembly language.(I wrote on an INTEL syntax, I use DosBOX to run it)
Fill page 0 of the video memory with any text. In the first line of the screen, display 16 NUL characters (ASCII code 0) with different values of the background attribute (the highest tetrad of the symbol attribute). Press the left button in the first line to select and store the selected color. When the cursor is placed on any character of the remaining lines of the screen, the color of the character will change to the selected one by pressing the left button.
BUT The program changes the color of a symbol other than the one I clicked on with my cursor.
.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
I suppose that maybe the problem is a two byte shift (but on the other hand I tried different variants and could not get the right result)
shr cx, 2 ; Shift CX right by 2 bits (divide by 4, because each character takes up 4 bytes)
Is expected that when you click on the cursor on the color will be selected , and after clicking on the symbol , it will change to the selected color
shr cx, 2 ; Shift CX right by 2 bits (divide by 4, because each character takes up 4 bytes)
The reasoning in the comment is wrong! Shifting twice is only shortcut-arithmatic.
Because the mouse driver gives you CX = 8 * Column, we need a division by 8, and because for every charactercell in the video memory we use a word, we need to multiply by 2. The combo means we need to divide by 4 which a double shift to the right can do most efficiently.
imul di, 20 ; Multiply the value of DI by 20 (the width of the window in characters)
The reasoning in the comment is wrong! Multiplying by 20 is only shortcut-arithmatic.
Because the mouse driver gives you DX = 8 * Row, we need a division by 8, and because for every row of characters in the video memory we use 160 bytes, we need to multiply by 160. The combo means we need to multiply by 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)
These operation aren't preserving anything! And in the latter case, it is the root problem of why "the program changes the color of a symbol other than the one I clicked on with my cursor".
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:
See that with es:[di+1]
you can address the attribute byte directly.
And imul di, dx, 20
(one of the finest x86 instructions around) does DI = DX * 20.