Ann*_*nna 1 x86 assembly dos x86-16
我是初学者,需要将 16 位二进制数转换为十六进制数的帮助。我已经完成了大部分代码,但我需要一些帮助。
输入示例:
1010101111001101
预期输出:
A B C D
电流输出:
AAAC
这是我的代码:
.MODEL SMALL
.STACK 1000h
.DATA
title db 'Convert BIN to HEX:.',13,10,'$'
HEX_Map DB '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
HEX_Out DB "00", 13, 10, '$' ; string with line feed and '$'-terminator
.CODE
main PROC
mov ax, @DATA ; Initialize DS
mov ds, ax
mov ah, 0
mov al, 3 ;clearing
int 10h
mov ah, 9
lea dx, title
int 21h ;displays title
mov dx, 0
loop16:
mov cx, 16 ;loop goes 16 Times because I need 16 bit binary input
mov bx, 0
;here I'm checking if input numer is 0 or 1, but it doesn't work as I want
read:
mov ah, 10h
int 16h
cmp al, '0'
jb read
cmp al, '1'
ja read10
read10:
mov ah, 0eh
int 10h
sub al, 48 ;conversion, sub 48 from ascii since 0 is on 48th place in ascii, but I'm not sure if this part is must to be or not
jmp end_loop
end_loop:
mov ah, 0 ;ah=0 so we can add ax to bx
add bx, ax
loop read
push bx ;here I push bx on stack, bx is as my input number
mov al, 13
mov ah, 0eh
int 10h
mov al, 10
mov ah, 0eh
int 10h
mov di, OFFSET HEX_Out ; First argument: pointer
pop bx ;Here I take input number from stack
mov ax, bx
call IntegerToHexFromMap ; Call with arguments
mov ah, 09h ; Int 21h / 09h: Write string to STDOUT
mov dx, OFFSET HEX_Out ; Pointer to '$'-terminated string
int 21h ; Call MS-DOS
mov ah, 10h
int 16h
mov ax, 4C00h ; Int 21h / 4Ch: Terminate program (Exit code = 00h)
int 21h ; Call MS-DOS
main ENDP
IntegerToHexFromMap PROC
mov si, OFFSET Hex_Map ; Pointer to hex-character table
mov bx, ax ; BX = argument AX
and bx, 00FFh ; Clear BH (just to be on the safe side)
shr bx, 1
shr bx, 1
shr bx, 1
shr bx, 1 ; Isolate high nibble (i.e. 4 bits)
mov dl, [si+bx] ; Read hex-character from the table
mov [di+0], dl ; Store character at the first place in the output string
mov bx, ax ; BX = argument AX
and bx, 00FFh ; Clear BH (just to be on the safe side)
shr bx, 1
shr bx, 1
shr bx, 1
shr bx, 1 ; Isolate high nibble (i.e. 4 bits)
mov dl, [si+bx] ; Read hex-character from the table
mov [di+1], dl ; Store character at the first place in the output string
mov bx, ax ; BX = argument AX
and bx, 00FFh ; Clear BH (just to be on the safe side)
shr bx, 1
shr bx, 1
shr bx, 1
shr bx, 1 ; Isolate high nibble (i.e. 4 bits)
mov dl, [si+bx] ; Read hex-character from the table
mov [di+2], dl ; Store character at the first place in the output string
mov bx, ax ; BX = argument AX (just to be on the safe side)
and bx, 00FFh ; Clear BH (just to be on the safe side)
and bl, 0Fh ; Isolate low nibble (i.e. 4 bits)
mov dl, [si+bx] ; Read hex-character from the table
mov [di+3], dl ; Store character at the second place in the output string
ret
IntegerToHexFromMap ENDP
IntegerToHexCalculated PROC
mov si, OFFSET Hex_Map ; Pointer to hex-character table
mov bx, ax ; BX = argument AX
shr bl, 1
shr bl, 1
shr bl, 1
shr bl, 1 ; Isolate high nibble (i.e. 4 bits)
cmp bl, 10 ; Hex 'A'-'F'?
jl .1 ; No: skip next line
add bl, 7 ; Yes: adjust number for ASCII conversion
.1:
add bl, 30h ; Convert to ASCII character
mov [di+0], bl ; Store character at the first place in the output string
mov bx, ax ; BX = argument AX
shr bl, 1
shr bl, 1
shr bl, 1
shr bl, 1 ; Isolate high nibble (i.e. 4 bits)
cmp bl, 10 ; Hex 'A'-'F'?
jl .2 ; No: skip next line
add bl, 7 ; Yes: adjust number for ASCII conversion
.2:
add bl, 30h ; Convert to ASCII character
mov [di+1], bl ; Store character at the first place in the output string
mov bx, ax ; BX = argument AX
shr bl, 1
shr bl, 1
shr bl, 1
shr bl, 1 ; Isolate high nibble (i.e. 4 bits)
cmp bl, 10 ; Hex 'A'-'F'?
jl .3 ; No: skip next line
add bl, 7 ; Yes: adjust number for ASCII conversion
.3:
add bl, 30h ; Convert to ASCII character
mov [di+2], bl ; Store character at the first place in the output string
mov bx, ax ; BX = argument AX (just to be on the safe side)
and bl, 0Fh ; Isolate low nibble (i.e. 4 bits)
cmp bl, 10 ; Hex 'A'-'F'?
jl .4 ; No: skip next line
add bl, 7 ; Yes: adjust number for ASCII conversion
.4:
add bl, 30h ; Convert to ASCII character
mov [di+3], bl ; Store character at the second place in the output string
ret
IntegerToHexCalculated ENDP
END main ; End of assembly with entry-procedure
Run Code Online (Sandbox Code Playgroud)
当您将位收集到 中时,不能用于int 10h (0e)char 输出bx。该int调用需要bl设置为文本的前景色并bh指向文本页面。
此外,bx您还将计算数字的数量,而不是输入的数字。在调试器(你的原始代码)中尝试一下,在后面放置断点loop并输入(盲目地,如果它没有显示)例如“1100110011001100”,bx将是8(如果有人int调用 destroy bx,我可能是错的,我没有运行它,就在我的脑海里)。
因此,为了修复您的输入部分,我会改为int 21h, 2显示字符,如下所示(还修复了结果的累积bx):
; read 16 bits from keyboard ('0'/'1' characters accepted only)
mov cx, 16 ; loop goes 16 Times because I need 16 bit binary input
xor bx, bx ; result number (initialized to zero)
read:
mov ah, 10h
int 16h ; read character from keyboard
cmp al, '0'
jb read ; ASCII character below '0' -> re-read it
cmp al, '1'
ja read ; ASCII character above '1' -> re-read it
mov dl,al ; keep ASCII for output in DL
shr al,1 ; turn ASCII '0'(0x30)/'1'(0x31) into CF=0/1 (Carry Flag)
rcl bx,1 ; enrol that CF into result from right (and shift previous bits up)
mov ah,2 ; output character in DL on screen
int 21h
loop read ; read 16 bits
Run Code Online (Sandbox Code Playgroud)
我没有检查其余的代码,因为如果我检查的话,我会非常渴望完全重写它,所以暂时只保留输入部分。
调试器应该允许您每次单步执行一条指令(或者在任何行上放置断点,然后运行到该行为止)。
因此,您可以在每个步骤之后检查寄存器和内存中的值。
例如,如果您将断点放在add bx,ax原始代码之前,您应该能够在调试器中读取(在按下“1”键并且调试器中断后add):
ax是 1(根据按下的按键),并且bx从 0 到“1”按键的计数(在进一步的迭代中)。
在按下四次“1”键后,您应该很明显,bx等于4(0100二进制)与 相距甚远1111,因此有些东西无法按您想要的方式工作,您必须从“我想在那里写的内容”重新调整”到“我真正写的内容”,再次阅读您的代码并了解需要更改哪些内容才能获得预期结果。
在您的情况下,例如shl bx,1在之前添加指令add解决这种情况(将旧位“向上”移动一个位置,将最低有效位设置为零,即“准备好添加斧头”)。
继续努力尝试调试器的东西,如果不弄清楚调试器,在汇编中几乎不可能做任何事情。或者在这里继续问,看到什么,不明白什么。这对于汇编编程来说确实是绝对必要的。
其他选择就是在你的头脑中“模拟”CPU,并运行带有帮助注释的屏幕上的指令(我强烈建议纸质的,PC 不知何故不适合我)。这比使用调试器要困难和繁琐得多。可能需要数周/数月的时间才能开始“模拟”而不会出现太多错误,因此您通常会在第一次尝试时发现错误。从好的方面来说,这将使您深入了解 CPU 的工作原理。
我将尽力帮助您理解您手头的内容,并从原始代码中找出一些错误来演示如何使用它。
所以你有 16 位数字,例如:
1010 1011 1100 1101 (unsigned decimal 43981)
Run Code Online (Sandbox Code Playgroud)
我在每组 4 位之间放置了空格(很少称为“半字节”),因为有一个有趣的事实。每个半字节准确地形成单个十六进制数字。所以上面的数字是十六进制的:
A B C D (10, 11, 12, 13)
Run Code Online (Sandbox Code Playgroud)
检查每个十六进制数字与上面的 4 位的对应关系。
因此,您想要的是将原始 16b 值分解为四个 4 位数字,从最高有效位到最低有效位(b12-b15、b8-b11、b4-b7、b0-b3 => 16 位数字中的特定位:“b15 b14 b13 ... b2 b1 b0”)。
每个这样的数字的值为 0-15(因为它们是 4 位,并且使用所有可能的组合),因此您希望将其转换为 ASCII 字符'0'-'9'对于值 0-9,以及'A'-'F'对于值 10-15。
每个转换后的值都存储到内存缓冲区的下一个字节位置,因此最终它们形成字符串“ABCD”。
这听起来可能“显而易见”,但它是第 2 部分内部计算的完整描述,因此请确保您真正理解每个步骤,以便您可以随时对照此检查代码并搜索差异。
现在我将向您展示我在第二部分中看到的一些错误,并尝试将其与上面的“理论”联系起来。
首先是数据和结构:
HEX_Out DB "00", 13, 10, '$'
Run Code Online (Sandbox Code Playgroud)
这会编译为字节:('0', '0', 13, 10, '$'或30 30 0D 0A 24当查看为十六进制字节时)。
如果你把'A', 'B', 'C', 'D'它写下来,你能发现问题吗?
您只为数字保留了两个字节(通过“00”),但您写入了四个字节,因此也
13将10被覆盖。
现在IntegerToHexFromMap,从代码来看,您似乎不明白andand 的shr作用(搜索按位运算解释)。
您从 中提取前三个字符相同的 b4-b7 位bx (copy of ax),然后为第四个字母提取 b0-b3 位。因此,这是您尝试将 8 位转换代码扩展到 16 位,但您没有提取正确的位。
我将尝试对它的第一部分进行广泛的评论,让您了解您做了什么。
; bx = 16 bit value, mark each bit as "a#" from a0 to a15
and bx, 00FFh
; the original: a15 a14 a13 ... a2 a1 a0 bits get
; AND-ed by: 0 0 0 ... 1 1 1
; resulting into bx = "a7 to a0 remains, rest is cleared to 0"
shr bx, 1
; shifts bx to right by one bit, inserting 0 into top bit
; bx = 0 0 0 0 0 0 0 0 0 a7 a6 a5 a4 a3 a2 a1 (a0 is in CF)
shr bx, 1
; shifts it further
; bx = 0 0 0 0 0 0 0 0 0 0 a7 a6 a5 a4 a3 a2 (a1 is in CF)
shr bx, 1
; bx = 0 0 0 0 0 0 0 0 0 0 0 a7 a6 a5 a4 a3 (a2 ...)
shr bx, 1
; bx = 0 0 0 0 0 0 0 0 0 0 0 0 a7 a6 a5 a4
; so if bx was value 0x1234 at the beginning, now bx = 0x0003
; conversion to ASCII and write is OK.
Run Code Online (Sandbox Code Playgroud)
因此,您将位 b4-b7 作为第一个字符,但需要位 b12-b15。我希望你能完全理解这一点,我知道一开始你可能会感到困惑,哪一位是哪一位,为什么有时右边有一些东西,然后左边有一些东西。
位的命名通常是从最低有效位(值 2 0 = 1,所以我称之为“b0”)到最高有效位(如果是 16 位数字,则值 2 15 = 32768,我称之为“b15”)。
但由于数字原因,位是从最高有效位到最低有效位(以二进制数形式)写入的,因此“左侧”的位以 b15 开头,“右侧”的位以 b0 结尾。
向右移动意味着将b_i移动到b_(i-1),这实际上将其值减半,因此shr value,1也可以视为无符号除以二。
向左移动是从b_i到b_(i+1),有效地将值乘以二(指令shl和sal,两者都产生相同的结果,因为两者的 b0 都设置为零)。
sar是“算术”右移,保持最高有效位的值完整(符号位),因此对于-1(所有位均为 1)它将再次产生-1,对于所有其他数字,它的工作原理是有符号除以 2。
顺便说一句,从 80286 CPU 开始你就可以使用shr bx,4(也可以看作除以 16 = 2*2*2*2)。你真的被迫为 8086 编码吗?那么可能值得加载cl4 个并执行shr bx,cl,而不是 4 个shr bx,1。四行相同的台词让我很恼火。
另外,如果您已经了解了什么and,那么现在这对您来说一定看起来很荒谬:
and bx, 00FFh ; why not 0Fh already here???
and bl, 0Fh
Run Code Online (Sandbox Code Playgroud)
现在考虑一下如何提取第一个字符的位 b12-b15 以及如何修复您的IntegerToHexFromMap.
最后,我将向您展示如何重写它以使代码非常短,我的意思是源代码,但也包括二进制大小。(为了性能,我会编写不同的代码,而不是针对 8086,但这个应该可以在 8086 上运行):
警告 - 尝试按照上述建议自行修复您的版本。只有当你有固定版本时,然后看看我的代码,作为 30 年前如何编写某些东西的新想法的灵感。另外,如果您正在做学校作业,请确保您可以从头说出有关XLAT指令的所有内容,因为作为一名讲师,我会对任何使用此指令的学生高度怀疑,它是完整的历史,并且由于编译器不使用它,这是显而易见的该代码是由人类编写的,并且可能是有经验的人。
IntegerToHexFromMap PROC
; ax = number to convert, di = string buffer to write to
; modifies: ax, bx, cx, dx, di
; copy of number to convert (AX will be used for calculation)
mov dx, ax
; initialize other helpful values before loop
mov bx, OFFSET HEX_Map ; Pointer to hex-character table
mov cx, 00404h ; for rotation of bits and loop counter
; cl = 4, ch = 4 (!) Hexadecimal format allows me
; to position the two "4" easily in single 16b value.
FourDigitLoop: ; I will do every digit with same code, in a loop
; move next nibble (= hexa digit) in DX into b0-b3 position
rol dx, cl
; copy DX b0-b3 into AL, clear other bits (AL = value 0-15)
mov al, dl
and al, 0Fh
; convert 0-15 in AL into ASCII char by special 8086 instruction
; designed to do exactly this task (ignored by C/C++ compilers :))
xlat
; write it into string, and move string pointer to next char
mov [di],al
inc di
; loop trough 4 digits (16 bits)
dec ch
jnz FourDigitLoop
ret
IntegerToHexFromMap ENDP
Run Code Online (Sandbox Code Playgroud)
如果你只是使用这段代码而不理解它是如何工作的,上帝会杀死一只小猫......你不希望这样,对吧?
最后免责声明:我没有任何 16 位 x86 环境,所以我在没有测试的情况下编写了所有代码(我只是有时尝试编译它,但语法必须类似于 NASM,所以我不会为这个 MASM/ 这样做) TASM/emu8086 来源)。因此,可能存在一些语法错误(甚至可能是功能错误?:-O),如果您无法使其工作,请发表评论。
| 归档时间: |
|
| 查看次数: |
5510 次 |
| 最近记录: |