我提供了两种不同的计算字符串长度时切换段的实现。我不明白为什么我们要切换额外段和数据段,或者更准确地说,将数据段(寄存器 DS)的内容复制到额外段(寄存器 ES)。如果我删除这些行push ds
,pop es
结果不会改变,但是当我les di, [bp+4]
用替换时mov di, [bp+4]
,屏幕上会显示消息,但带有一堆其他不同的字符。
为什么会发生这种情况?我是装配方面的新手。
org 0x0100
jmp start
message: db 'Hello world!', 0 ; First string to be concatenated
; Subroutine to calculate the length of a string
; Takes the segment and offset of a string as parameters
strlen:
push bp
mov bp, sp
push es
push cx
push di
les di, [bp+4] ; Point es:di to string
mov cx, 0xffff ; Load maximum number in cx
xor al, al ; Load a zero in al
repne scasb ; Find zero in the string
mov ax, 0xffff ; Load maximum number in ax
sub ax, cx ; Find change in cx
dec ax ; Exclude null from length
pop di
pop cx
pop es
pop bp
ret 4
; Subroutine to print a string
; Takes the x position, y position, attribute, and address of a null-terminated string as parameters
printstr:
push bp
mov bp, sp
pusha
push di
push ds ; Push segment of string
mov ax, [bp+4]
push ax ; Push offset of string
call strlen ; Calculate string length
cmp ax, 0 ; Is the string empty?
jz exit ; No printing if string is empty
mov cx, ax ; Save length in cx
mov ax, 0xb800 ; Video memory base address
mov es, ax ; Point es to video base
mov al, 80 ; Load al with columns per row
mul byte [bp+8] ; Multiply with y position
add ax, [bp+10] ; Add x position
shl ax, 1 ; Turn into byte offset
mov di, ax ; Point di to required location
mov si, [bp+4] ; Point si to string
mov ah, [bp+6] ; Load attribute in ah
cld ; Clear direction flag for auto-increment mode
nextchar:
lodsb ; Load next char in al
stosw ; Print char/attribute pair
loop nextchar ; Repeat for the whole string
exit:
pop ds
pop di
popa
pop bp
ret 8
start:
mov ax, 30
push ax ; Push x position
mov ax, 20
push ax ; Push y position
mov ax, 0x7 ; Blue on white attribute
push ax ; Push attribute
mov ax, message
push ax ; Push address of message
call printstr ; Call the printstr subroutine
mov ax, 0x4c00 ; Terminate program
int 0x21
替代版本:
push bp
mov bp, sp
pusha
push ds
pop es ; load ds in es
mov di, [bp+4] ; point di to string
mov cx, 0xffff ; load maximum number in cx
xor al, al
repne scasb
mov ax, 0xffff
sub ax, cx
dec ax
jz done
mov cx, ax
...
关键在于,程序中的段寄存器从一开始就彼此相等。这是因为您正在创建一个 .COM 程序(
ORG 0x100
这样做)。所有 DS 或 ES 的更改实际上都不算数,在这种情况下也没有必要。scasb
为了使用始终依赖于 ES:DI 的指令,需要设置 ES 段寄存器。现在,从strlen 子例程 ( ) 返回的方式与从printstr 子例程 ( )
ret 4
退出的方式存在重要的不匹配。exit:
pop ds
ret 4
(丢弃远指针的偏移量和段部分),那么您需要删除它pop ds
。pop ds
那么您需要写入ret 2
(仅丢弃远指针的偏移部分)。提示:printstr确实会将 ES 更改为不同于 DS(将其设置为 0xB800)。您通常会将 ES 保留在堆栈上。