我试图在 AAM 指令之后使用 AX 中存储的值将其除以 2,为什么它不适用于 2 位数字输出?

2024-04-25

英语不是我的母语;请原谅输入错误,我将在此处显示的代码是一项作业。

我真的需要了解发生了什么事。我在 DosBox 0.74 和 TASM 汇编器中使用 Intel 8086 语法。

当我必须除以 2 时,代码的问题在于三角形的面积。注意:程序始终只从键盘读取 1 位数字。看代码(这里的一个用户建议显示我的所有代码而不是部分显示,我认为有点长,但我认为他是对的,更具可读性)[注:我编辑了这个问题,只编辑了部分有问题的代码在这里,我花了一些时间将注释和消息翻译成英文,这有点长,我没有太多时间全部翻译]:

;Segmento de Datos
DATA SEGMENT 'DATA'
    LADO1   DB ?                        ;con el ? se indica que la variable no tiene nada
    LADO2   DB ?
    LADO3   DB ?
    BASE    DB ?
    ALTURA  DB ?
    COUNTER DB 0                        ;contador para muestra de mensajes

    ;mensajes ESPECIFICOS
    MSG_PRESENTACION DB 10,13,"Este programa calcula los perimetros y areas de algunas figuras geometricas $"

    ;triangulo
    MSG_DIMENSIONES_TRIANGULO   DB 10,13,10,13,"Ingrese las dimensiones del triangulo (considere el lado 2 como la BASE)$"
    MSG_PERIMETRO_TRIANGULO     DB 10,13,"El perimetro del triangulo es: $"
    MSG_AREA_TRIANGULO          DB 10,13,"El area del triangulo es: $"
    ;triangulo

    ;cuadrado
    MSG_DIMENSIONES_CUADRADO    DB 10,13,10,13,"Ingrese las dimensiones del cuadrado$"
    MSG_PERIMETRO_CUADRADO      DB 10,13,"El perimetro del cuadrado es: $"
    MSG_AREA_CUADRADO           DB 10,13,"El area del cuadrado es: $"
    ;cuadrado

    ;rectangulo
    MSG_DIMENSIONES_RECTANGULO  DB 10,13,10,13,"Ingrese las dimensiones del rectangulo$"
    MSG_PERIMETRO_RECTANGULO    DB 10,13,"El perimetro del rectangulo es: $"
    MSG_AREA_RECTANGULO         DB 10,13,"El area del rectangulo es: $"
    ;rectangulo

    ;otros mensajes GENERALES
    MSG_ALTURA  DB 10,13,"Introduzca la altura: $"  ;sirve para solicitar la altura de varias figuras
    MSG_BASE    DB 10,13,"Introduzca la base: $"
    MSG_LADO    DB 10,13,"Ingrese el lado $"
    MSG_PUNTO   DB ": $"                ;sirve para colocar 2 puntos luego del lado
    MSG_PRUEBA  DB 10,13,10,13,"El valor de prueba es: $"
DATA ENDS

;Segmento de codigo
CODE SEGMENT 'CODE'
    ASSUME DS:DATA,CS:CODE              ;se le indica al Assembler que DATA es el nombre dado al DATA SEGMENT, y CODE el nombre de CODE SEGMENT

MAIN PROC                               ;incia el inicio del segmento de codigo
    MOV AX,DATA                         ;hacer disponible/accesible el contenido del DATA SEGMENT al CODE SEGMENT
    MOV DS,AX                           ;inicializar el data segment, debido a limitacion de MOV, se hace con la ayuda de AX

    LEA DX,MSG_PRESENTACION             ;Mensaje de presentacion / introduccion
    CALL printString

    ;triangulo********
    LEA DX,MSG_DIMENSIONES_TRIANGULO    ;carga a DX el mensaje 1 (Se le dice al usuario que introduzca los datos del triangulo)
    CALL printString
    ;SOLICITUD DE DATOS     ----//----
    LEA DX,MSG_ALTURA                   ;indica al usuario que introduzca la altura del triangulo
    CALL printString
    CALL readNumber                     ;se lee del teclado la entrada del usuario mediante este procedimiento (1 digito)
    MOV ALTURA,AL

    LEA DX,MSG_LADO                     ;se le dice al usuario que introduzca el lado 1
    CALL printString
    CALL numberInMessage                ;muestra el numero 1 despues del mensaje anterior y DOS PUNTOS para introducccion de datos (solo con fines de formato)
    CALL readNumber
    MOV LADO1,AL                        ;mueve valor capturado a la variable

    LEA DX,MSG_LADO                     ;se le dice al usuario que introduzca el lado 2 (la base)
    CALL printString
    CALL numberInMessage                ;muestra el numero 2 despues del mensade de LADO
    CALL readNumber
    MOV LADO2,AL                        ;mueve valor capturado a la variable

    LEA DX,MSG_LADO                     ;se le dice al usuario que introduzca el lado 3
    CALL printString
    CALL numberInMessage                ;muestra el numero 3
    CALL readNumber
    MOV LADO3,AL                        ;mueve valor capturado a la variable

    ;PERIMETRO DE TRIANGULO ----//----
    ADD AL,LADO2                        ;SUMA LADO3(AL) CON CON LADO2 y lo guarda en AL
    ADD AL,LADO1                        ;SUMA LO ANTERIOR CON LADO1
    MOV AH,0                            ;borra basura de AH
    AAA                                 ;significa ASCII Adjust after Addition
    MOV BX,AX                           ;los registros AH y AL se usan una y otra vez, por tanto para evitar perder sus datos se mueven a BH y BL, esto se hace moviendo AX a BX
    LEA DX,MSG_PERIMETRO_TRIANGULO      ;lee la direccion efectiva de memoria del mensaje (resultado de la suma) y la carga en DX
    CALL printString                    ;printDigits usa BX para la impresion de los datos
    CALL printDigits                    ;imprime resultado

    ;AREA DE TRIANGULO      ----//----
    MOV AL,LADO2                        ;multiplicar la base (LADO2) por la altura
    MUL ALTURA
    AAM
    MOV BL,2
    AAD
    DIV BL
    MOV BX,AX

    LEA DX,MSG_AREA_TRIANGULO           ;mensaje que indica la impresion del area del triangulo
    CALL printString
    CALL printDigits                    ;imprime numeros del area del triangulo
    ;FIN triangulo********

    ;cuadrado********
    LEA DX,MSG_DIMENSIONES_CUADRADO
    CALL printString
    ;SOLICITUD DE DATOS     ----//----
    LEA DX,MSG_LADO
    CALL printString
    LEA DX,MSG_PUNTO                    ;solo con fines de formato, muestra los 2 puntos antes la entrada del usuario
    CALL printString
    CALL readNumber                     ;recordemos que la lectura se almacena en AL
    MOV LADO1,AL

    ;PERIMETRO DEL CUADRADO ----//----
    MOV AH,4                            ;MUL no acepta valores constantes como argumentos, por tanto se mueve a un registro
    MUL AH                              ;multiplicamos la entrada del usuario por 4 y se almacena en AL
    MOV AH,0                            ;se limpia basura de AH
    AAM                                 ;ya explicado, se iran reduciendo comentarios debido a esto, no se omitiran valores y/o situaciones especificas y necesarias para entender
    MOV BX,AX                           ;se mueve porque se trabaja mucho con AX, y evitamos posible perdida de los datos
    LEA DX,MSG_PERIMETRO_CUADRADO
    CALL printString
    CALL printDigits                    ;imprime lo que esta en BX

    ;AREA DEL CUADRADO      ----//----
    MOV AL,LADO1
    MUL AL                              ;multiplicamos la entrada del usuario por el mismo, ya que se desea elevar al cuadrado
    MOV AH,0                            ;se limpia basura de AH
    AAM
    MOV BX,AX                           ;se mueve porque se trabaja mucho con AX, y evitamos posible perdida de los datos
    LEA DX,MSG_AREA_CUADRADO
    CALL printString
    CALL printDigits                    ;imprime lo que esta en BX
    ;FIN cuadrado********

    ;rectangulo********
    LEA DX,MSG_DIMENSIONES_RECTANGULO
    CALL printString
    ;SOLICITUD DE DATOS     ----//----
    LEA DX,MSG_BASE
    CALL printString
    CALL readNumber
    MOV BASE,AL

    LEA DX,MSG_ALTURA
    CALL printString
    CALL readNumber
    MOV ALTURA,AL

    ;PERIMETRO DEL RECTANGULO----//----
    MOV AH,2                            ;no se puede usar MUL con una consatnte, por tanto se mueve a un registro
    MUL AH                              ;AL contiene la ALTURA, y se multiplica por 2
    MOV AH,0                            ;se limpia la basura
    AAM
    MOV BX,AX                           ;se guarda en otro registro para evitar perdida de datos

    MOV AL,BASE
    MOV AH,2                            ;mismo procedimiento que arriba para multiplicar por un numero constante
    MUL AH
    MOV AH,0
    AAM

    ADD BX,AX                           ;se suma el primer resultado (guardado en BX), con lo ultimo conseguido (guardo en AX), el resultado queda e BX
    AAA

    LEA DX,MSG_PERIMETRO_RECTANGULO
    CALL printString
    CALL printDigits

    ;AREA DEL RECTANGULO    ----//----
    MOV AL,BASE
    MUL ALTURA
    MOV AH,0
    AAM
    MOV BX,AX
    LEA DX,MSG_AREA_RECTANGULO
    CALL printString
    CALL printDigits
    ;FIN rectangulo********
    MOV AH,4CH                          ;EXIT, termina el programa
    INT 21H                             ;ejecuta la interrupcion
MAIN ENDP                               ;acaba la rutina principal

;PROCEDIMIENTOS -----------------------------------------------------------------------------------------------------------------------------
printDigits PROC
    ADD BX,3030H                        ;como AH & AL (BH & BL) estan en BCD, se le suman 30 para obtener el valor en ASCII
    ;primer digito
    MOV AH,2                            ;se le indica a la interrupcion que haremos una impresion de un CARACTER
    MOV DL,BH                           ;como BH contiene el primer digito, se imprime este primero pasando el valor a DL
    INT 21H                             ;se llama a la interrupcion, que ejecuta lo especificado por AH, tomando como entrada DL
    ;segundo digito
    MOV AH,2                            ;nuevamente se le indica una impresion (STANDARD OUTPUT)
    MOV DL,BL                           ;esta vez se pasa el segundo digito
    INT 21H                             ;y se imprime
    RET
printDigits ENDP

printString PROC
    MOV AH,9                            ;se pasa a AH el 9 para indicar que se hara una salida a STANDARD OUTPUT
    INT 21H                             ;interrupcion del DOS, que funciona con el valor del registro AH, el 9 indica STANDARD OUTPUT
    RET
printString ENDP

readNumber PROC
    MOV AH,1                            ;1 en AH indica STANDARD INPUT
    INT 21H                             ;Se usa la interrupcion para tomar entrada del teclado (por instruccion 1 en AH), se retorna lectura a AL
    SUB AL,30H                          ;se convierte de ASCII a numero normal para realizar las operaciones deseadas, el 30 es porque se recibe el numero en BCD desempacado
    RET
readNumber ENDP

numberInMessage PROC
    ADD COUNTER,1                       ;añade 1 a al contador, para nuevos usos es necesario limpiar COUNTER antes de la llamada
    MOV BL,COUNTER                      ;se mueve porque se desea imprimir el contador, y printDigits trabaja con BX
    MOV BH,0
    CALL printDigits
    LEA DX,MSG_PUNTO                    ;contiene el mensaje de los dos puntos para formato solamente
    CALL printString
    RET
numberInMessage ENDP

CODE ENDS                               ;final del segmento de codigo
END MAIN

注意:“AREA DE TRANGULO”是唯一有问题的部分。

这不适用于所有数字(那些输出为 2 位数字),我认为这里的问题是 AAM 和 AAD 指令,AAD 要求 AX 有一个 2 位打包的 BCD 数字,所以我决定删除 AAM 指令,并且那也不行。所以我想知道我在这里缺少什么,这样做得到的输出是(使用 5 和 6,5*6=30/2=15):

The area of the triangle is: 0?

我认为 AAD 指令是问题的一部分,如果我使用其他数字,例如 4 和 4,输出将是:

The area of the triangle is: 08

所以,我决定删除AAD指令,但行为是一样的,你能告诉我为什么吗?

预先感谢您的时间和帮助! (如果您认为在我的代码中还有另一种方法可以做某事,并且出于任何原因更好,请告诉我,我想学习)


您需要了解 AAM 和 AAD 的实际用途。 AAM 指令将 AX 中 00-99 范围内的二进制数转换为 AH 和 AL 中的两个未压缩的十进制数字。 AAD 执行此操作的相反操作,它将 AH 和 AL 中的两个解包十进制数字转换为 AX 中 00-99 范围内的二进制数。这就是它们所做的全部事情,尽管它们的名字表明它们只能分别在乘法之后和除法之前使用。

用于计算三角形面积的代码以 0 到 9 范围内的两个独立的二进制数开始,然后将它们相乘以获得 0 到 81 范围内的二进制数。然后使用 AAM 转换二进制数将 AX 中的结果转换为 AH 和 AL 中的两个解压缩数字。然后,它几乎立即使用 AAD 将 AH 和 AL 中的解包数字转换回 AX 中的二进制数。

希望您能够看到,先使用 AAM 再使用 AAD 并不会产生任何有用的效果。您从 AX 中的二进制数开始,最终在 AX 中得到相同的二进制数。您要做的是将两个维度相乘,然后除以二,最后将结果转换为单独的数字以进行打印。所以你应该做的是这样的:

MOV AL,LADO2
MUL ALTURA
MOV BL,2
DIV BL
AAM

请注意,AAM 不遵循乘法指令并不重要。不管怎样,它都会进行相同的转换。

请注意,您可以利用二进制右移与无符号除以二相同的事实来简化此代码:

MOV AL,LADO2
MUL ALTURA
SHR AX, 1
AAM

最后,我要指出的是,现代代码不使用 AAM 和 AAD 等指令,因为它们比使用更通用指令的等效操作要慢。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我试图在 AAM 指令之后使用 AX 中存储的值将其除以 2,为什么它不适用于 2 位数字输出? 的相关文章

  • 不知道如何一次打印整个日历[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 cseg segment assume cs cseg ds cseg org 100H begin mov es cs video mov
  • 什么是微编码指令?

    我看过很多参考微编码指令的文献 这些是什么以及为什么使用它们 CPU 读取机器代码并将其解码为内部控制信号 将正确的数据发送到正确的执行单元 大多数指令映射到一个内部操作 并且可以直接解码 例如 在 x86 上 add eax edx只是将
  • x86 实模式下的段大小

    我对实模式下段的大小有一个疑问 因为它们不能超过 64K 但可以小于那个 http support microsoft com kb 120069 我的问题是这些段大小和基地址是如何初始化的 就像保护模式下有 GDT 和 LDT 一样 实模
  • 现代 x86 硬件不能将单个字节存储到内存中吗?

    说到 C 的并发内存模型 Stroustrup 的C 编程语言 第 4 版 第 1 节 41 2 1 说 就像大多数现代硬件一样 机器无法加载或存储小于单词的任何内容 然而 我的 x86 处理器已经有几年的历史了 它可以并且确实存储小于单词
  • gcc 如何知道内联汇编中使用的寄存器大小?

    我有内联汇编代码 define read msr index buf asm volatile rdmsr d buf 1 a buf 0 c index 使用该宏的代码 u32 buf 2 read msr 0x173 buf 我发现反汇
  • 推送 64 位 intel osx

    我想将 64 位地址压入堆栈 如下所示 asm pushq 0x1122334455667788 但我得到编译错误 我只能按以下方式推送 asm pushq 0x11223344 有人可以帮助我理解我的错误吗 我是装配新手 所以如果我的问题
  • 汇编编程语言:程序仅当输入为 ESC 时退出,并在退出前要求确认(y/n),否则循环

    我只是汇编语言编程的初学者 我们的第一个任务是让程序仅在输入为 ESC 时退出 退出之前请求确认 y n 否则循环 我知道 ESC 在 ASCII 代码中具有等效值 但我对插入位置或是否需要添加更多内容感到困惑 请帮我 这是程序 model
  • C/C++ 中的简单“Hello World”内联汇编语言程序

    我使用 devcpp 和 borland c 编译器 asm mov ax 4 I O Func mov bx 1 Output func mov cx name address of the string mov dx 6 length
  • ARM 调用约定是否允许函数不将 LR 存储到堆栈中?

    正如标题所示 我在理解 ARM 架构的调用约定时遇到问题 特别是 我仍然很难知道当你调用子程序时 LR 寄存器会发生什么 我认为 当您进入子程序时 处理 LR 寄存器的最明显 最安全的方法是将其存储到堆栈中 但该行为没有出现在文档中 因此我
  • 使用 gdb 调试反汇编库

    在Linux和Mac OS X中可以使用strapi和next来调试应用程序而无需调试信息 在 Mac OS X 上 gdb 显示在库内部调用的函数 尽管有时会在每个 stepi 指令中推进多个汇编程序指令 在 Linux 上 当我进入动态
  • 汇编中如何计算负数

    我是汇编新手 我有一个关于如何表示负数的问题 我有三个 DWORDS 变量 比方说 result DWORD 0 i DWORD 3 j DWORD 5 我想计算这个公式 结果 i j 8 但是 当我执行 i j 时 由于符号 结果将是一个
  • 在 REP MOVSW 之前 PUSH CS / POP DS 的目的是什么?

    为什么在下面的代码中我们压入代码段 PUSH CS 然后将其弹出到数据段 POP DS 我将这些行明确指定为 line1 和 line2 请告诉我 MOVSW 在这里是如何工作的 IF HIGHMEMORY PUSH DS MOV BX D
  • 一条指令可以同时处于两种寻址模式吗?

    我在书中读到了以下内容从头开始编程 处理器有多种不同的访问数据的方式 称为 寻址模式 最简单的模式是立即模式 其中 要访问的数据嵌入在指令本身中 例如 如果我们想将寄存器初始化为 0 而不是给出 计算机要从中读取 0 的地址 我们将指定立即
  • x86-64 上这个语句有什么问题?

    该函数的目的是获取堆栈的起始地址 unsigned long find start void asm movq rsp eax 当我编译它时 出现错误 Error suffix or operands invalid for movq mo
  • 遍历内存编辑每个字节

    我正在编写汇编代码 提示用户输入一串小写字符 然后输出包含所有大写字符的相同字符串 我的想法是迭代从特定地址开始的字节 并从每个字节中减去 20H 将小写变为大写 直到到达具有特定值的字节 我对 Assembly 相当缺乏经验 所以我不确定
  • 如何在汇编语言中换行打印多个字符串

    我试图在汇编中的不同行上打印多个字符串 但使用我的代码 它只打印最后一个字符串 我对汇编语言非常陌生 所以请耐心等待 section text global start start mov edx len mov edx len1 mov
  • 汇编基础知识:输出寄存器值

    我刚刚开始学习汇编语言 我已经陷入了 在屏幕上显示存储在寄存器中的十进制值 的部分 我使用 emu8086 任何帮助将不胜感激 model small Specifies the memory model used for program
  • 减法进位标志

    我正在使用 MASM32 有了这个代码 mov eax 5 sub eax 10 CF 状态标志将被设置 但使用我的铅笔和纸 我实际上看到 MSB 没有任何进位 是的 我知道从较少的数字中减去大的数字集CF 但我想知道为什么 因为使用这段代
  • 从汇编程序获取命令行参数

    通读 专业汇编语言书籍 似乎它提供了用于读取命令行参数的错误代码 我纠正了一点 现在它从段错误变成了读取参数计数 然后是段错误 这是完整的代码 data output1 asciz There are d params n output2
  • 在 x86-64 CPU 上通过交叉修改代码重现意外行为

    Question 对于可能在 x86 或 x86 x64 系统上触发意外行为的交叉修改代码有哪些想法 在这些系统中 交叉修改代码中的所有操作均已正确完成 但在执行处理器之前执行序列化指令除外修改代码 如下所述 我有一个 Core 2 Duo

随机推荐