MASM x86汇编中的32位加法和减法

时间:2013-11-13 19:03:15

标签: assembly x86 masm

我正在尝试编写一个程序,它可以添加和减去两个32位数字,并存储内存中的总和和差异。我没有任何输出,只是通过调试器获得结果。

这是我的代码。

;---------------------------------------------------------;
;**********************************************************
STACK   SEGMENT   PARA STACK 'STACK'
        DB 64 DUP('STACK')
STACK   ENDS
;**********************************************************
DSEG    SEGMENT   PARA PUBLIC 'DATA'
X1      DD   4967290
X2      DD   4967295
SUM     DD   ?
DIFF    DD ?
DSEG    ENDS
;**********************************************************
;---------------------------------------------------------
CSEG     SEGMENT   PARA PUBLIC 'CODE'
OUR_PROG PROC  FAR
         ASSUME CS:CSEG, DS:DSEG, SS:STACK
;  Set up the stack to contain the proper values 
;  so this program can return to debug.
;  
         PUSH DS           ; Put return seg. addr on stack
         MOV EAX,0         ; Clear a register EAX
         PUSH EAX          ; Put zero return address on stack

;  Initialize the data segment address
         MOV EAX,DSEG      ;Initialize DS
         MOV DS,AX
;  -------------------------------------------------------------
;  Perform the addition
;
         MOV EAX,X1        ; Load 32 bit variable in X1 to reg AX
         MOV EBX,X2        ; Load 32 bit variable in X2 to reg BX
         ADD EAX,EBX       ; Add data in registers AX and BX, store in AX
;  Store the sum in memory
;
         MOV SUM,EAX       ; Store the result at mem loc SUM
;  -------------------------------------------------------------
;  Perform the subtraction
         MOV EAX,X1       ; Reload first word to reg EAX
         CMP EAX,EBX      ; Compare values of X1 and X2 stored in registers EAX and EBX
         JL  .SWAPSUB     ; If EBX is greater than EAX, jump to SWAPSUB
         JL  .NOSWAP      ; If ''                     , jump past other sub section

.SWAPSUB:                 ;Jump point to swap values

         XCHG EAX,BX      ; Swap values of EAX and EBX

.NOSWAP:         
         SUB EAX,EBX      ; Subtract EBX from EAX
         MOV DIFF,EBX     ; Store the result at mem loc DIFF         

         RET              ; Retrurn to DEBUG
OUR_PROG ENDP
CSEG     ENDS
         END OUR_PROG
;**********************************************************

我不太了解Assembly,但我正在使用DOSBOX,MASM 5.10和链接器程序来构建我的代码。

我似乎遇到的问题是,当我尝试构建代码时,它表示EAXEBX未定义。对于Illegal size for operandMOV的{​​{1}}次来电,它还会SUM说。

任何人都可以告诉我我做错了什么或更简单的方法吗?我一直试图弄清楚几个小时,但收效甚微。

谢谢!

1 个答案:

答案 0 :(得分:1)

您可以使用.386指令在16位程序中访问32位寄存器。但是它仍然是一个16位程序!

在机器代码中,通过在指令编码中添加显式前缀(66h-“操作数大小前缀”)来访问32位寄存器。但是,未使用.386伪指令的.MODEL强制MASM假定32位程序正在运行,而不是每次都切换到32位。在这种情况下,您必须明确告诉汇编器使用16位段,因此它将假定代码将以16位模式执行:

STACK   SEGMENT   PARA STACK 'STACK' USE16
...
DSEG    SEGMENT   PARA PUBLIC 'DATA' USE16
...
CSEG    SEGMENT   PARA PUBLIC 'CODE' USE16
...

继续通过组合16位段部分和16位偏移量部分来形成存储器地址。 “ RETF”(“ PROC FAR”)从堆栈中获取两个16位值,然后跳到那里。

更改

PUSH DS           ; Put return seg. addr on stack
MOV EAX,0         ; Clear a register EAX
PUSH EAX          ; Put zero return address on stack

PUSH DS           ; Put PSP seg. addr on stack
MOV AX,0          ; Clear a register AX
PUSH AX           ; Put zero return address on stack

段地址保持16位。因此,32位EAX具有不同的操作数大小。

更改

MOV EAX,DSEG        ;Initialize DS
MOV DS,AX

MOV AX,DSEG         ;Initialize DS
MOV DS,AX

通常,同一条指令中不能使用不同的操作数大小(16位和32位)。

更改

XCHG EAX,BX         ; Swap values of EAX and EBX

XCHG EAX,EBX        ; Swap values of EAX and EBX

在16位操作(MS-DOS)期间可以短时间切换到完整的32位操作(保护模式),但这非常复杂并且容易出错,因此不建议使用。

JL  .SWAPSUB        ; If EBX is greater than EAX, jump to SWAPSUB
JL  .NOSWAP         ; If ''                     , jump past other sub section

不会执行您期望的操作。在进行了签名比较后,JL(如果不多,则跳转)跳转。因此,0xDEADBEEF(负)将小于0x1FEDBEEF(正)。在这种情况下,EAX和EBX交换错误,SUB将导致负数,而DIFF将错误。顺便说一句,第二个JL将永远不会跳转,您的意思是JNL(如果不小于,则跳转)。 CMP也会执行 unsigned 比较。因此,您可以在与JB进行无符号比较之后进行跳转(如果低于,则跳转)。仅使用一个条件跳转就足够了(JNB-如果不低于此条件,则跳转),这可能会跳过下一条指令。

更改

JL  .SWAPSUB        ; If EBX is greater than EAX, jump to SWAPSUB
JL  .NOSWAP         ; If ''                     , jump past other sub section

JNB .NOSWAP         ; If EAX not below EBX, skip over .SWAPSUB

SUB EAX,EBX之后,区别在于EAX,而不是EBX

利用MASM 5.1和更高版本的可能性的示例:

.MODEL SMALL, C                 ; 'C' also enables local labels and variables in MASM 5.1
.386                            ; Enable features of the i386 processor
.STACK 1000h                    ; Reserve space for stack and initialize stack pointer

.DATA
    X1          DD  1FEDBEEFh       ; 535674607
    X2          DD  0DEADBEEFh      ; 3735928559
    SUM         DD  ?
    DIFF        DD  ?
    SUM_STR     DB 16 DUP ('$')
    DIFF_STR    DB 16 DUP ('$')
    S           DB "SUM: $"
    D           DB "DIFF: $"
    CrLf        DB 13, 10, '$'

.CODE
OUR_PROG PROC

;  -------------------------------------------------------------
;  Initialize the data segment address
         MOV AX, @DATA          ; Initialize DS
         MOV DS, AX

;  -------------------------------------------------------------
;  Perform the addition
;
         MOV EAX, X1            ; Load 32 bit variable in X1 to reg EAX
         MOV EBX, X2            ; Load 32 bit variable in X2 to reg EBX
         ADD EAX, EBX           ; Add data in registers AX and BX, store in EAX

         MOV SUM, EAX           ; Store the result at mem loc SUM

;  -------------------------------------------------------------
;  Perform the subtraction
        MOV EAX, X1             ; Reload first word to reg EAX

        CMP EAX, EBX            ; Compare values of X1 and X2 stored in registers EAX and EBX
        JNB SHORT @F
        XCHG EAX, EBX           ; Swap values of EAX and EBX
        @@:
        SUB EAX, EBX            ; Subtract EBX from EAX
        MOV DIFF, EAX           ; Store the result at mem loc DIFF

;  -------------------------------------------------------------
;  Display the stuff

        MOV EAX, SUM            ; "SUM: 4271603166"
        LEA DI, SUM_STR
        CALL eax2dec
        LEA DX, S
        MOV AH, 9               ; http://www.ctyme.com/intr/rb-2562.htm
        INT 21h
        LEA DX, SUM_STR
        INT 21h
        LEA DX, CrLf
        Int 21h

        MOV EAX, DIFF           ; "DIFF: 3200253952"
        LEA DI, DIFF_STR
        CALL eax2dec
        LEA DX, D
        MOV AH, 9               ; http://www.ctyme.com/intr/rb-2562.htm
        INT 21h
        LEA DX, DIFF_STR
        INT 21h
        LEA DX, CrLf
        Int 21h

;  -------------------------------------------------------------
;  Exit the program

        MOV AX, 4C00h           ; Exit with code 0
        INT 21h                 ; Call MSDOS
OUR_PROG ENDP

;  -------------------------------------------------------------
;  Convert an integer in EAX to a decimal ASCII string

eax2dec PROC                    ; ARG EAX DWORD, DI: offset of string
LOCAL Regs[4]:DWORD

    IF (@Version EQ 510)        ; Bug in MASM 5.1: Wrong offset with `Regs`
        mov [bp-16], eax        ; Preserve some registers
        mov [bp-12], ebx
        mov [bp-8], ecx
        mov [bp-4], edx
    ELSE
        mov [Regs+0], eax
        mov [Regs+4], ebx
        mov [Regs+8], ecx
        mov [Regs+12], edx
    ENDIF

        xor cl, cl              ; Counter
        mov ebx, 10             ; Base 10
    @@:
        xor edx,edx             ; Clear EDX for division
        div ebx                 ; EAX/10 remainder EDX (eff. DL)
        push dx                 ; Onto the stack to get it later LIFO
        inc cl                  ; Increment counter
        test eax, eax           ; Anything more to divide?
        jnz @B                  ; Yes - jump to the @@ before
    @@:
        pop ax                  ; Get back the remainders LIFO
        or al, 30h              ; Convert to ASCII
        mov [di], al            ; Store it
        inc di                  ; Pointer to ASCII string
        dec cl                  ; Decrement pointer
        jnz @B                  ; If remainders left, jump to the @ before

        mov BYTE PTR [di], '$'  ; Terminator for INT 21h/09h

    IF (@Version EQ 510)        ; Bug in MASM 5.1: Wrong offset with `Regs`
        mov eax, [bp-16]        ; Restore some registers
        mov ebx, [bp-12]
        mov ecx, [bp-8]
        mov edx, [bp-4]
    ELSE
        mov eax, [Regs+0]
        mov ebx, [Regs+4]
        mov ecx, [Regs+8]
        mov edx, [Regs+12]
    ENDIF

        ret                     ; RET: DI points to the terminating '$'
eax2dec ENDP

;**********************************************************
END OUR_PROG                    ; END of source &  entry point