这个打印十六进制数的函数(用汇编写的)有什么问题?
What is wrong with this function (written in assembly) to print hexadecimal numbers?
我只是一个汇编初学者,正在阅读 Nick Blundell 的 OS 书时,遇到了编写一个可以打印十六进制数的函数的问题。但是,尽管多次验证逻辑,我似乎无法找到为什么这段代码不起作用。请帮助,我将不胜感激。
HEX_OUT: db '0x0000', 0
MASK: dw 0b1111000000000000
COUNTER: db 3
print_string :
pusha ;SAVES ALL REGISTER VALUES TO BE RESTORED WHEN RETURNING.
mov ah, 0x0e
jmp print_loop ;NOT COMPULSORY
print_loop :
mov al, [bx]
add bx, 1 ;ADD 1, NOT 8, NOT 16.
int 0x10
cmp al, 0 ;SETS A FLAG ACCORDING TO RESULT OF COMPARISON.
jne print_loop ;CAUSES LOOP.
jmp final_block ;CAN BE REPLACED BY THE STATEMENTS IN final_block, NO NEED FOR MAKING NEW LABEL.
final_block :
popa
ret ;RETURNS TO THE POINT WHERE CALL HAPPENED.
print_hex :
pusha
mov bx, HEX_OUT
add bx, 2
alter_loop : ;LOOP TO ALTER HEX_OUT
mov ax, [MASK]
cmp ax, 0 ;CONDITION TO END LOOP
je after_loop
mov ax, dx ;GETTING(INTO AX) THE DATA FOR N-TH POSITION
and ax, [MASK]
mov cx, [COUNTER]
shift_loop :
cmp cx, 0
je end_shift_loop
shr ax, 4
sub cx, 1
end_shift_loop:
cmp ax, 0x0009 ;DO HEX->ALPHABET IF NUMBER IS GREATER THAN 9
jle skip_hex_to_alphabet
add ax, 39 ;EQUIVALENT TO (sub ax, 48--- sub ax, 9 ---add ax, 96)
skip_hex_to_alphabet :
add ax, 48 ;ADDING 48(ASCII OF 0), IS ALREADY SUBTRACTED IF N-TH NUMBER>9
mov [bx], al ;STORING DATA IN LOCATION POINTED TO BY BX
add bx, 1 ;INCREMENT FOR LOOP
mov ax, [MASK] ;CHANGING MASK
shr ax, 4
mov [MASK], ax
mov ax, [COUNTER] ;UPDATING COUNTER
sub ax, 1
mov [COUNTER], ax
jmp alter_loop
after_loop :
mov bx, HEX_OUT
call print_string
popa
ret
调用函数时:-
mov dx, 0x1fd6
call print_hex
它打印 0xWGd0
而不是 0x1fd6
.
您错过了返回 shift_loop
的跳转并且错误地声明了 COUNTER
的大小。
因为你用了mov cx, [COUNTER]
,所以COUNTER
一定是一个词,修正一下:
COUNTER: dw 3
最后,您没有正确移动屏蔽值。在第一次迭代中,and ax, [MASK]
生成 0x1000
,而在 shift_loop
中,它减少为 0x0100
,因为它只迭代一次。
用跳转结束循环:
shift_loop :
cmp cx, 0
je end_shift_loop
shr ax, 4
sub cx, 1
jmp shift_loop
end_shift_loop:
我的两分钱:我已经编写和阅读汇编二十多年了,你的代码让我感到困惑。我没想到十六进制打印例程会遍历静态掩码并将结果存储在静态字符串中。对于给定的任务来说太复杂了。
您可以简单地使用减少四位的可变移位计数器和一个(常数)掩码来提取半字节。然后,您甚至可以使用 16 字节查找 table 将半字节转换为字符,避免分支。
此外,由于您正在为 DOS 编程,因此非常值得在线查找 TASM 的副本并使用其调试器(td
- Turbo Debugger)。
很容易为变量使用错误的大小并使用垃圾,调试器会立即向您展示这一点。
如果您喜欢具体示例,这里有一个简单的实现。
;AX = number to print in hex
hex:
mov cx, 12 ;Shift counter, we start isolating the higher nibble (which starts at 12)
mov bx, hexDigits ;Lookup tables for the digitals
mov dx, ax ;We need a copy of the number and AX is used by the int10 service
.extract:
mov si, dx ;Make a copy of the original number so we don't lose it. Also we need it in SI for addressing purpose
shr si, cl ;Isolate a nibble by bringing it at the lower position
and si, 0fh ;Isolate the nibble by masking off any higher nibble
mov al, [bx + si] ;Transform the nibble into a digit (that's why we needed it in SI)
mov ah, 0eh ;You can also lift this out of the loop. It put it here for readability.
int 10h ;Print it
sub cx, 4 ;Next nibble is 4 bits apart
jnc .extract ;Keep looping until we go from 0000h to 0fffch. This will set the CF
ret
hexDigits db "0123456789abcdef"
我只是一个汇编初学者,正在阅读 Nick Blundell 的 OS 书时,遇到了编写一个可以打印十六进制数的函数的问题。但是,尽管多次验证逻辑,我似乎无法找到为什么这段代码不起作用。请帮助,我将不胜感激。
HEX_OUT: db '0x0000', 0
MASK: dw 0b1111000000000000
COUNTER: db 3
print_string :
pusha ;SAVES ALL REGISTER VALUES TO BE RESTORED WHEN RETURNING.
mov ah, 0x0e
jmp print_loop ;NOT COMPULSORY
print_loop :
mov al, [bx]
add bx, 1 ;ADD 1, NOT 8, NOT 16.
int 0x10
cmp al, 0 ;SETS A FLAG ACCORDING TO RESULT OF COMPARISON.
jne print_loop ;CAUSES LOOP.
jmp final_block ;CAN BE REPLACED BY THE STATEMENTS IN final_block, NO NEED FOR MAKING NEW LABEL.
final_block :
popa
ret ;RETURNS TO THE POINT WHERE CALL HAPPENED.
print_hex :
pusha
mov bx, HEX_OUT
add bx, 2
alter_loop : ;LOOP TO ALTER HEX_OUT
mov ax, [MASK]
cmp ax, 0 ;CONDITION TO END LOOP
je after_loop
mov ax, dx ;GETTING(INTO AX) THE DATA FOR N-TH POSITION
and ax, [MASK]
mov cx, [COUNTER]
shift_loop :
cmp cx, 0
je end_shift_loop
shr ax, 4
sub cx, 1
end_shift_loop:
cmp ax, 0x0009 ;DO HEX->ALPHABET IF NUMBER IS GREATER THAN 9
jle skip_hex_to_alphabet
add ax, 39 ;EQUIVALENT TO (sub ax, 48--- sub ax, 9 ---add ax, 96)
skip_hex_to_alphabet :
add ax, 48 ;ADDING 48(ASCII OF 0), IS ALREADY SUBTRACTED IF N-TH NUMBER>9
mov [bx], al ;STORING DATA IN LOCATION POINTED TO BY BX
add bx, 1 ;INCREMENT FOR LOOP
mov ax, [MASK] ;CHANGING MASK
shr ax, 4
mov [MASK], ax
mov ax, [COUNTER] ;UPDATING COUNTER
sub ax, 1
mov [COUNTER], ax
jmp alter_loop
after_loop :
mov bx, HEX_OUT
call print_string
popa
ret
调用函数时:-
mov dx, 0x1fd6
call print_hex
它打印 0xWGd0
而不是 0x1fd6
.
您错过了返回 shift_loop
的跳转并且错误地声明了 COUNTER
的大小。
因为你用了mov cx, [COUNTER]
,所以COUNTER
一定是一个词,修正一下:
COUNTER: dw 3
最后,您没有正确移动屏蔽值。在第一次迭代中,and ax, [MASK]
生成 0x1000
,而在 shift_loop
中,它减少为 0x0100
,因为它只迭代一次。
用跳转结束循环:
shift_loop :
cmp cx, 0
je end_shift_loop
shr ax, 4
sub cx, 1
jmp shift_loop
end_shift_loop:
我的两分钱:我已经编写和阅读汇编二十多年了,你的代码让我感到困惑。我没想到十六进制打印例程会遍历静态掩码并将结果存储在静态字符串中。对于给定的任务来说太复杂了。
您可以简单地使用减少四位的可变移位计数器和一个(常数)掩码来提取半字节。然后,您甚至可以使用 16 字节查找 table 将半字节转换为字符,避免分支。
此外,由于您正在为 DOS 编程,因此非常值得在线查找 TASM 的副本并使用其调试器(td
- Turbo Debugger)。
很容易为变量使用错误的大小并使用垃圾,调试器会立即向您展示这一点。
如果您喜欢具体示例,这里有一个简单的实现。
;AX = number to print in hex
hex:
mov cx, 12 ;Shift counter, we start isolating the higher nibble (which starts at 12)
mov bx, hexDigits ;Lookup tables for the digitals
mov dx, ax ;We need a copy of the number and AX is used by the int10 service
.extract:
mov si, dx ;Make a copy of the original number so we don't lose it. Also we need it in SI for addressing purpose
shr si, cl ;Isolate a nibble by bringing it at the lower position
and si, 0fh ;Isolate the nibble by masking off any higher nibble
mov al, [bx + si] ;Transform the nibble into a digit (that's why we needed it in SI)
mov ah, 0eh ;You can also lift this out of the loop. It put it here for readability.
int 10h ;Print it
sub cx, 4 ;Next nibble is 4 bits apart
jnc .extract ;Keep looping until we go from 0000h to 0fffch. This will set the CF
ret
hexDigits db "0123456789abcdef"