Reputation: 6547
I am trying to write some code which displays some text at a given position on the screen.
When doing some research, I found this page that shows the formula position = (y_position * characters_per_line) + x_position;
.
Here is the snipped of code that calculates and sets the position:
set_cursor_pos:
push ax
push bx
mov al, [ypos]
mov bl, 80
mul bl
add ax, [xpos]
mov bl, 2
mul bl
mov di, ax
pop bx
pop ax
ret
This works until ypos = 3
and xpos = 15
After this, it seems to wrap around to the beginning. Hear are some examples:
y=2, x = 30:
y=0, x = 60:
y=3, x=15:
y=4, x=0:
As you can see, my algorithm works until y=3, x-15
. After that, it wraps around.
Is this because there is not enough memory? Do I need to enable the A20 line? Is it another problem? If so, please can you explain what and why.
Finally, here is all. of my code:
org 0x7c00
mov ax, 0xB800
mov es, ax
xor di, di
cli
mov ah, 0Fh
mov si, msg
call set_cursor_pos
call print
hlt
print:
cli
lodsb
stosw
cmp al, 0
jne print
ret
set_cursor_pos:
push ax
push bx
mov al, [ypos]
mov bl, 80
mul bl
add ax, [xpos]
mov bl, 2
mul bl
mov di, ax
pop bx
pop ax
ret
msg db 'Hello, World', 0
xpos db 0
ypos db 4
times 510-($-$$) db 0
dw 0xaa55
Upvotes: 1
Views: 85
Reputation: 364170
Look at your operand-sizes. xpos
is only 1 byte, but you're reading 2 bytes with add ax, [xpos]
. Also, mul bl
does ax = al * bl
, so you're throwing away the high half of the mul-by-80 result.
i.e. set_cursor_pos
returns with
di = (( (80*ypos) & 0xff) + (xpos + (ypos<<8)) ) & 0xFF) * 2
From your previous questions, you're targeting a 386-compatible, so you could write it with
movzx di, byte [ypos]
imul di, di, 80
movzx ax, byte [xpos]
add di, ax
add di, di ; di *= 2. equivalent to shl di, 1 but more efficient.
(80 = 16 * 5, so you can also avoid imul
and use one lea di, [edi + edi*4]
/ shl di, 4
. Or whatever 8086-compatible trick for multiply by a number with so few set bits.)
There is zero point in using mul
to multiply by 2, unless you were going to use mul bx
and use the full 32-bit result in dx:ax
. But even then, for 2 you should just use add di,di
/ setc al
because the carry-out can only be 1 bit.
If xpos
and ypos
were 16-bit, you could use them as memory operands:
imul di, [ypos], 80
add di, [xpos]
add di, di ; di *= 2
Or of course you could keep them in registers in the first place.
Upvotes: 2