是什么导致文本显示在随机位置?
What is causing text to be displayed in a random place?
我正在尝试编写一些代码,在屏幕上的给定位置显示一些文本。
在做一些研究时,我发现 this page 显示了公式 position = (y_position * characters_per_line) + x_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
这一直有效到 ypos = 3
和 xpos = 15
在此之后,它似乎回到了开头。听听一些例子:
y=2, x=30:
y=0, x=60:
y=3, x=15:
y=4, x=0:
如您所见,我的算法一直有效到 y=3, x-15
。之后,它环绕。
是不是内存不足?我需要启用 A20 线路吗?这是另一个问题吗?如果是这样,请您解释一下是什么以及为什么。
终于到此为止。我的代码:
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
看看你的操作数大小。 xpos
只有 1 个字节,但你用 add ax, [xpos]
读取了 2 个字节。此外,mul bl
执行 ax = al * bl
,因此您丢弃了 80 倍乘法结果的高半部分。
即set_cursor_pos
returns 和
di = (( (80*ypos) & 0xff) + (xpos + (ypos<<8)) ) & 0xFF) * 2
根据你之前的问题,你的目标是 386 兼容,所以你可以用
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,所以你也可以避免 imul
并使用一个 lea di, [edi + edi*4]
/ shl di, 4
。或者任何与 8086 兼容的乘以数字的技巧几个设置位。)
使用 mul
乘以 2 是零点,除非您打算使用 mul bx
并在 dx:ax
中使用完整的 32 位结果。但即便如此,对于 2 你应该只使用 add di,di
/ setc al
因为进位只能是 1 位。
如果 xpos
和 ypos
是 16 位的,您可以将它们用作内存操作数:
imul di, [ypos], 80
add di, [xpos]
add di, di ; di *= 2
或者你当然可以首先将它们保存在寄存器中。
我正在尝试编写一些代码,在屏幕上的给定位置显示一些文本。
在做一些研究时,我发现 this page 显示了公式 position = (y_position * characters_per_line) + x_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
这一直有效到 ypos = 3
和 xpos = 15
在此之后,它似乎回到了开头。听听一些例子:
y=2, x=30:
y=0, x=60:
y=3, x=15:
y=4, x=0:
如您所见,我的算法一直有效到 y=3, x-15
。之后,它环绕。
是不是内存不足?我需要启用 A20 线路吗?这是另一个问题吗?如果是这样,请您解释一下是什么以及为什么。
终于到此为止。我的代码:
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
看看你的操作数大小。 xpos
只有 1 个字节,但你用 add ax, [xpos]
读取了 2 个字节。此外,mul bl
执行 ax = al * bl
,因此您丢弃了 80 倍乘法结果的高半部分。
即set_cursor_pos
returns 和
di = (( (80*ypos) & 0xff) + (xpos + (ypos<<8)) ) & 0xFF) * 2
根据你之前的问题,你的目标是 386 兼容,所以你可以用
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,所以你也可以避免 imul
并使用一个 lea di, [edi + edi*4]
/ shl di, 4
。或者任何与 8086 兼容的乘以数字的技巧几个设置位。)
使用 mul
乘以 2 是零点,除非您打算使用 mul bx
并在 dx:ax
中使用完整的 32 位结果。但即便如此,对于 2 你应该只使用 add di,di
/ setc al
因为进位只能是 1 位。
如果 xpos
和 ypos
是 16 位的,您可以将它们用作内存操作数:
imul di, [ypos], 80
add di, [xpos]
add di, di ; di *= 2
或者你当然可以首先将它们保存在寄存器中。