Assembly 用DOS显示数字
我的任务是编写一个程序,显示我的地址的线性 程序的PSP。我写了以下内容:Assembly 用DOS显示数字,assembly,dos,x86-16,integer-division,signed-integer,8086,Assembly,Dos,X86 16,Integer Division,Signed Integer,8086,我的任务是编写一个程序,显示我的地址的线性 程序的PSP。我写了以下内容: ORG 256 mov dx,Msg mov ah,09h ;DOS.WriteStringToStandardOutput int 21h mov ax,ds mov dx,16 mul dx ; ->
ORG 256
mov dx,Msg
mov ah,09h ;DOS.WriteStringToStandardOutput
int 21h
mov ax,ds
mov dx,16
mul dx ; -> Linear address is now in DX:AX
???
mov ax,4C00h ;DOS.TerminateWithExitCode
int 21h
; ------------------------------
Msg: db 'PSP is at linear address $'
我搜索了DOS api(使用)
并且没有找到一个函数来输出一个数字!
我错过了吗?我能做什么
我想以十进制显示
DX:AX中的数字。的确,DOS不提供直接输出数字的功能。
您必须首先自己转换数字,然后让DOS显示它
使用其中一个文本输出函数
显示AX中保留的无符号16位数字
在处理转换数字的问题时,有助于了解
构成数字的数字相互关联。
让我们考虑数字65535及其分解:
(6 * 10000) + (5 * 1000) + (5 * 100) + (3 * 10) + (5 * 1)
(0 * 10000) + (0 * 1000) + (2 * 100) + (5 * 10) + (5 * 1)
方法1:按10的幂递减除法
处理从左到右的数字很方便,因为它
允许我们在提取后立即显示单个数字
- 通过将数字(65535)除以10000,我们得到一个单位数的商
(6) 我们可以直接输出为一个字符。我们还得到了一个余数
(5535)这将成为下一步的股息
- 通过将上一步(5535)的余数除以1000,我们得到
我们可以直接作为字符输出的一位商(5)。
我们还得到一个余数(535),它将成为下一步的股息
- 通过将上一步(535)的余数除以100,我们得到
我们可以直接作为字符输出的一位商(5)。
我们还得到一个余数(35),它将成为下一步的股息
- 通过将上一步(35)的余数除以10,我们得到
我们可以直接作为字符输出的一位商(3)。
我们还得到一个余数(5),它将成为下一步的红利
- 通过将上一步(5)的余数除以1,我们得到
我们可以直接作为字符输出的一位商(5)。
在这里,余数将始终为0。(避免这个愚蠢的除以1
需要一些额外的代码)
虽然这种方法当然会产生正确的结果,但它有一些缺点
缺点:
- 考虑较小的数字255及其分解:
(6 * 10000) + (5 * 1000) + (5 * 100) + (3 * 10) + (5 * 1)
(0 * 10000) + (0 * 1000) + (2 * 100) + (5 * 10) + (5 * 1)
如果我们使用相同的5步流程,我们会得到“00255”。那两个人领先
零是不需要的,我们必须包含额外的指令才能得到
摆脱他们
- 分隔器随着每一步的变化而变化。我们必须把一份除法器的清单储存在
记忆。动态计算这些分割线是可能的,但会引入
很多额外的部门
- 如果我们想将这种方法应用于显示更大的数字
32位,我们希望最终,相关部门将
真有问题
因此,方法1不切实际,因此很少使用
方法2:除以常数10
处理从右向左的数字似乎违反直觉
因为我们的目标是先显示最左边的数字。但当你即将
找出答案,它工作得很好
- 通过将数字(65535)除以10,我们得到一个商(6553),该商将
成为下一步的红利。我们还得到了一个余数(5)
现在还不能输出,所以我们必须在某个地方保存。堆栈是一个
这样做很方便
- 通过将上一步(6553)的商除以10,我们得到
将在下一步中成为红利的商(655)。我们也得到
我们还不能输出的余数(3),因此我们必须保存它
某处。堆栈是这样做的一个方便的地方
- 通过将上一步(655)的商除以10,我们得到
将在下一步中成为红利的商(65)。我们也得到
我们还不能输出的余数(5),因此我们必须保存它
某处。堆栈是这样做的一个方便的地方
- 通过将上一步(65)的商除以10,我们得到
将在下一步中成为红利的商(6)。我们也得到
我们还不能输出的余数(5),因此我们必须保存它
某处。堆栈是这样做的一个方便的地方
- 通过将上一步(6)的商除以10,我们得到
表示这是最后一次除法的商(0)。我们也得到
可以直接输出为字符的余数(6),但是
事实证明,避免这样做是最有效的,因此,与以前一样,我们将
将其保存在堆栈上
在这一点上,堆栈保存了我们的5个余数,每个余数都是一个数字
范围为[0,9]的数字。由于堆栈是后进先出(后进先出),因此
我们将首先弹出的值是我们希望显示的第一个数字。我们使用
用5个POP
分开循环以显示完整的数字。但实际上,,
因为我们希望这个例程也能够处理
少于5位,我们将在数字到达时进行计数,稍后再进行计数
许多流行音乐
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
第二种方法没有第一种方法的缺点:
- 因为当商变为零时我们就停止了,所以从来没有任何问题 带难看的前导零
- 分隔器是固定的。这很容易
- 将此方法应用于显示较大的数字和数据非常简单 这正是下一步要做的
显示DX:AX中保留的无符号32位数字 在一个由2个除法组成的级联上,需要对中的32位值进行除法
DX:AX
10倍。第1部分除以高股息(扩展为0)
test dx,dx ;Sign bit is bit 15 of high word
jns .a ;It's a positive number
neg dx ;\
neg ax ; | Negate DX:AX
sbb dx,0 ;/
push ax dx ;(1)
mov dl,"-"
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
pop dx ax ;(1)
.a: mov bx,10 ;CONST
push bx ;Sentinel
.b: mov cx,ax ;Temporarily store LowDividend in CX
mov ax,dx ;First divide the HighDividend
xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is HighQuotient, Remainder is re-used
xchg ax,cx ;Temporarily move it to CX restoring LowDividend
div bx ; -> AX is LowQuotient, Remainder DX=[0,9]
push dx ;(2) Save remainder for now
mov dx,cx ;Build true 32-bit quotient in DX:AX
or cx,ax ;Is the true 32-bit quotient zero?
jnz .b ;No, use as next dividend
pop dx ;(2a) First pop (Is digit for sure)
.c: add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
pop dx ;(2b) All remaining pops
cmp dx,bx ;Was it the sentinel?
jb .c ;Not yet
; IN (al) OUT ()
DisplaySignedNumber8:
push ax
cbw ;Promote AL to AX
call DisplaySignedNumber16
pop ax
ret
; -------------------------
; IN (ax) OUT ()
DisplaySignedNumber16:
push dx
cwd ;Promote AX to DX:AX
call DisplaySignedNumber32
pop dx
ret
; -------------------------
; IN (dx:ax) OUT ()
DisplaySignedNumber32:
push ax bx cx dx
...
; IN (al) OUT () MOD (ax,dx)
DisplaySignedNumber8:
cbw
; --- --- --- --- -
; IN (ax) OUT () MOD (ax,dx)
DisplaySignedNumber16:
cwd
; --- --- --- --- -
; IN (dx:ax) OUT () MOD (ax,dx)
DisplaySignedNumber32:
push bx cx
...