简体   繁体   English

跟踪MASM的NCR组装程序

[英]Tracing a NCR assembly program of MASM

I missed the session when our lecturer explained it.. 当我们的讲师解释时,我错过了会议。

I know the formulae for NCR 我知道NCR的公式

NCR = N! NCR = N! / (R! * (NR)!) /(R!*(NR)!)

I am not following the NCR PROC , as no factorial is found, and some creepy recursive jobs are being done there. 我没有关注NCR PROC ,因为没有找到因子,并且那里正在进行一些令人毛骨悚然的递归工作。 Help would be really appreciated. 帮助将非常感激。


print macro str
    lea dx,msg
    mov ah,09h
    int 21h
endm

.model small
.data
    n dw 15
    r dw 6
    ncr dw 1
    msg db "The NCR is : ","$"
.code
     start: mov ax,@data
            mov ds,ax

            mov bx,n
            mov cx,r
            inc bx
            call ncr1
            print msg          
            mov ax,ncr
            call display

            mov ah,4ch
            int 21h

          ncr1 proc
                cmp cx,00      
                  je l1
                push cx         
                dec cx
                call ncr1        
                mov ax,bx       
                pop cx          
                sub ax,cx       
                mul ncr         
                div cx          
                mov ncr,ax      
              l1 : ret
          ncr1 endp

          display proc
                aam
                add ax,3030h
                mov bx,ax
                mov dl,bh
                mov ah,02h
                int 21h
                mov dl,bl
                mov ah,02h
                int 21h                
             ret
      display endp

end start                

Okay, I see some reuse value in showing how to analyze chunks of assembler code, so here it is. 好的,我在展示如何分析汇编代码块时看到了一些重用价值,所以在这里。


We're dealing with a subroutine here, a chunk of code that's supposed to be relatively autonomous. 我们在这里处理一个子程序,一段应该相对自治的代码。

So, first, let's determine the raw effect it has on the program: its inputs, outputs and effect on sp - ie its calling convention and signature . 所以,首先,让我们确定它对程序的原始影响:它的输入,输出和对sp - 即它的调用约定签名

Which entities does it use that are not set by it? 它使用哪些实体不是由它设定的?

ncr1 proc
    cmp cx,00    # <-- cx
      je l1
    push cx
    dec cx
    call ncr1
    mov ax,bx    # <--bx
    pop cx
    sub ax,cx
    mul ncr      # <--ncr
    div cx
    mov ncr,ax
  l1 : ret
ncr1 endp

Which entities does it modify and not restore afterwards? 它修改了哪些实体,之后没有恢复?

ncr1 proc
    cmp cx,00
      je l1
    push cx      # cx->local_stack[-2]
    dec cx       # -->cx? (overridden)
    call ncr1
    mov ax,bx
    pop cx       # local_stack[-2]->cx => cx restored
                 # (the next step is actually needed to deduce push/pop
                 # correspondence so they should be done in parallel)
    sub ax,cx
    mul ncr      # -->ax,dx
    div cx
    mov ncr,ax   # -->ncr
  l1 : ret
ncr1 endp

Only the last changes to the entities are marked (since they prevail over earlier ones). 仅标记实体的最后更改(因为它们优先于先前的更改)。

Does it have any net effect on sp ? 它对sp有任何净效应吗? (numbers are current sp relative to the return address) (数字是相对于返回地址的当前sp

ncr1 proc
    cmp cx,00
      je l1
    push cx     #-2
    dec cx
    call ncr1   #delta is same as self
    mov ax,bx
    pop cx      #0
    sub ax,cx
    mul ncr
    div cx
    mov ncr,ax
  l1 : ret      #without an argument, only cleans the return address
ncr1 endp

It doesn't (so "same as self" is 0), and 0 in all cases at ret confirms that it handles the local stack correctly. 它没有(因此“与self相同”为0),并且在ret所有情况下都为0 ,确认它正确处理本地堆栈。

Concluding, its signature is: 最后,它的签名是:

ncr1 (cx,bx,ncr) -> ax,dx,ncr

Where ax and dx are probably unused (but they are still volatile ). axdx可能未被使用(但它们仍然是不稳定的 )。 And the calling convention is custom pure register with one hardcoded in/out parameter. 调用约定是自定义纯寄存器,带有一个硬编码的输入/输出参数。


Now, all that leaves is to track which physical - and then, conceptual - value each entity holds at any time: 现在,剩下的就是跟踪每个实体在任何时候持有的物理 - 然后是概念 - 价值:

ncr1 proc  --initially: bx=N, cx=R (metavariables)
    cmp cx,00    # if R==0:
      je l1      #   ret (ax,dx,ncr unchanged)
    push cx      #[-2]=R
    dec cx       #cx=R-1
    call ncr1    #ncr1(N,R-1) -> ax,dx,ncr(probably the result)
    mov ax,bx    #ax=N
    pop cx       #cx=[-2]=R
    sub ax,cx    #ax=N-R
    mul ncr      #dx:ax=(N-R)*ncr = (N-R)*ncr1(N,R-1)
    div cx       #ax=[(N-R)*ncr1(N,R-1)]/R
    mov ncr,ax   #ncr=[(N-R)*ncr1(N,R-1)]/R
  l1 : ret
ncr1 endp        # -> ax,dx,ncr(the result, now we're sure)

Here it is: the procedure calculates (N,R) -> [(NR)*ncr1(N,R-1)]/R where N=bx , R=cx and the result is ncr (which is mutated). 这里是:程序计算(N,R) -> [(NR)*ncr1(N,R-1)]/R其中N=bxR=cx ,结果是ncr (其是变异的)。

Which looks suspicious: (NR) shall be (N+1-R) in the canonical formula as per comment62556150 . 看似可疑: (NR)在规范公式中应为(N+1-R) ,符合评论62556150 If we substitute n=N-1 , it'll become: (n+1,R) -> [(n+1-R)*ncr(n+1,R-1)]/R which looks okay (the first argument never changes)... so the procedure actually calculates nCr(n-1,r) . 如果我们用n=N-1代替,它将变成: (n+1,R) -> [(n+1-R)*ncr(n+1,R-1)]/R看起来没问题(第一个参数永远不会改变)...所以程序实际上计算nCr(n-1,r)

The choice to pass n+1 must be because n only goes into the formula as n+1 , so by this, we save cycles on increasing it each time. 传递n+1的选择必须是因为n只作为n+1进入公式,所以这样我们每次都会增加它的周期。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM