2016-11-13 5 views
3

영어는 모국어가 아닙니다. 입력 오류를 용서하십시오. 여기에 표시 할 코드는 숙제입니다.AX로 저장된 값을 AAM 명령 다음에 2로 나누기 위해 사용하려고합니다. 2 자리 숫자 출력에서 ​​작동하지 않는 이유는 무엇입니까?

나는 실제로 무슨 일이 일어나는지 이해할 필요가있다. 나는 DosBox 0.74 및 TASM 어셈블러에서 인텔 8086 구문을 사용하고 있습니다.

2로 나누어야 할 때 코드의 문제는 삼각형 영역에 있습니다. 참고 : 프로그램은 항상 키보드에서 한 자리 만 읽습니다. 코드를보십시오 (여기에있는 사용자는 부분적으로 보여주기보다는 모든 코드를 보여줄 것을 제안했습니다. 조금 길어 보입니다. 그러나 그가 옳다고 생각합니다. 더 읽기 쉽습니다) [참고 :이 질문을 편집 한 부분 만 문제가있는 코드가 여기에 있었다, 나는 영어의 의견과 메시지를 번역하는 시간이 좀 걸렸지의이 조금 이상, 나는 모든] 번역 할 너무 많은 시간이 없습니다 :

;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 TRIANGULO"는 문제가있는 유일한 부분입니다.

모든 숫자 (2 자리 숫자의 출력)에서는 작동하지 않으며 AAM 및 AAD 명령이 문제가 될 것이라고 생각합니다. AX는 2 자리 숫자의 BCD 번호를 필요로하므로 삭제하기로 결정했습니다. AAM 명령을 사용하면 작동하지 않습니다. 그래서 내가 여기에 누락 알고 싶어, 내가이 일을함으로써 얻을 출력 (5, 6 * 6 = 2분의 30 = 15 (5)을 사용하여)된다

The area of the triangle is: 0? 

나는 AAD 명령이 일부라고 생각합니다 내가 4, 4와 같은 다른 번호를 사용하는 경우 문제,이 출력 될 것입니다 : 그래서

The area of the triangle is: 08 

, 나는 AAD 명령을 삭제하기로 결정하지만, 동작은 동일합니다, 당신은 내게 이유를 알 수 있습니다 ?

시간과 도움을 미리 보내 주셔서 감사합니다. (내 코드에서 뭔가를 할 수있는 또 다른 방법이 있다고 생각하면 어떤 이유에서든 더 배우기를 원합니다)

+0

텍스트가 너무 많으면 더 간결하게 작성하면 더 많은주의를 기울일 수 있습니다. –

+0

실제로 사용중인 프로그램의 전체 소스 코드를 표시하도록 답을 편집하십시오. 그것을 분리 된 조각으로 자른 다음 순서를 뒤섞 지 마십시오. –

+0

이것은 AAM 등을 사용하기위한 [mcve]와 거의 비슷하지 않습니다. 그냥 몇 가지 값을 레지스터로 MOV하고 뭔가를하는 테스트 케이스를 만든다. 그런 다음 실제로 레지스터에있는 내용 (예 : 디버거를 사용하여 검사)을 설명하고 예상하고 싶었거나 예상했던 내용을 설명하십시오. [AAM] (http://www.felixcloutier.com/x86/AAM.html)은 'DIV r8'과 비슷하지만 즉각적인 피연산자가 있고 몫/나머지는 DIV보다 반대 레지스터에 있습니다. 당신의 프로그램이 작동하지 않는다면, 그것을 통해 단계별로 실행하고 당신이 예상했던 것을하지 않는 명령어에 대해서는 insn set reference를 참조하십시오. –

답변

4

실제로 AAM과 AAD가하는 일을 이해해야합니다. AAM 명령은 00-99 범위의 AX에있는 이진수를 AH 및 AL의 두 개의 언팩 십진수로 변환합니다. AAD는이 연산의 역 수행을 수행하여 AH 및 AL의 2 개의 언 패킹 십진수를 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와 같은 명령어는보다 범용적인 명령어를 사용하는 동일한 연산보다 느리기 때문에 사용하지 마십시오.

+0

재미있는 사실 : AAM은 Haswell에서'DIV r8'보다 1주기 빠릅니다. 물론, 모듈 곱셈 역함수는 어느 것보다 빠르며, AAM은'imm8' 상수 약수 만 지원하기 때문에 항상 옵션입니다. (이것은 10 일 필요는 없습니다. (피연산자없이 사용할 때 기본값입니다.) http : //www.felixcloutier.com/x86/AAM.html)). –

+0

@ PeterCordes 실제로 인텔 최적화 설명서를 확인했지만 나열되지 않았습니다. 아그너 안개 테이블에서 그걸 찾았 니? –

+0

그래, 그게 지시 타이밍의 가장 편리한 소스 야. Haswell : AAM은 8ups, 21c 대기 시간, 8c 처리량 당 하나입니다. DIV r8은 9 uops, 22-25c 대기 시간 및 9c 처리량 당 하나입니다. –