Assembly Simulator in Python

From dftwiki3
Revision as of 17:53, 24 November 2015 by Thiebaut (talk | contribs)
Jump to: navigation, search

--D. Thiebaut (talk) 17:49, 24 November 2015 (EST)




Video



Python Simulator


# assemblySimulator.py
# D. Thiebaut
# This program allows the display of the stack
# as a recursive factorial program computes the
# factorial of 3.
# The program writes the stack to a file, as well
# as the contents of the data registers to a file,
# so the best way to display them is to "watch" the
# files stack.txt and registers.txt in two separate
# terminal windows.   Make sure the "watch" linux
# command is installed on your system.

import sys, os

program="""
extern _printDec
extern _println        

;;;  -------------------------
;;;  code area
;;;  -------------------------
                section .text
                global  _start

;;; ; ---------------------------------------------------------
;;; ; main program
;;; ; ---------------------------------------------------------
_start:         mov     eax, 4  ; n = 4

                push    eax     ; TOS <-- XX
                push    eax     ; TOS <-- n
                call    fact
                pop     eax     ; get n in eax

;;;  return to OS
                mov     eax, 1
                mov     ebx, 0
                int     0x80

;;; ; ---------------------------------------------------------
fact:           push    ebp     ; TOS <-- old ebp
                mov     ebp, esp
                
;;;  test for stopping condition

                cmp     dword[ebp+8], 1    ; n==1 ?
                jg      .recurse
                mov     dword[ebp+12], 1 ; yes, then n! = 1
                pop     ebp     ; return to caller
                ret     4

;;;  recursive step

.recurse:       push    eax     ; TOS <-- old eax
                push    ebx     ; TOS <-- old ebx
                push    edx     ; TOS <-- old edx
        
                mov     eax, dword[ebp+8]  
                dec     eax     ; n-1 in eax
                push    eax     ; TOS <-- XX
                push    eax     ; TOS <-- n-1
                call    fact    ; recursive call
                pop     ebx     ; get fact(n-1) in ebx
                mov     eax, dword[ebp+8]
                mul     ebx     ; edx:eax <-- n * (n-1)!
        
                mov     dword[ebp+12], eax ; that's n!
                                ; drop edx
                pop     edx     ; restore regs
                pop     ebx
                pop     eax
        
                pop     ebp     ; return
                ret     4
"""

instructions = [ "ret", "call", "pop", "push", "dec", "mov",
                 "mul", "cmp", "jg", "int" ]
registers = {"eax":0, "ebx":0, "ecx":0, "edx":0, "esp":0, "ebp":0 }
stack = [0] * 40
flags = { "l":True, "lt":True, "e":True,
          "ne":True, "g":True, "ge":True }


class Listing:
    """The listing class.
    Initializes itself with the lines from an assembly program.
    and disassembles and executes the different instructions
    in the program.
    """
    def __init__( self, program ):
        self.lines = []
        for line in program.split( "\n" ):
            self.lines.append( line.rstrip() )
        self.stack = []
        self.lastLine = len( self.lines )-1
        
        self.ip = 0
        for i,line in enumerate( self.lines ):
            if line.find( "_start:" ) != -1:
                self.ip = i
                break

        # find all labels
        self.labelAddress = {}
        for i, line in enumerate( self.lines ):
            if line.find( ":" ) != -1:
                label = line.split( ":" )[0]
                self.labelAddress[label] = i
                
    def display( self ):
        for i, line in enumerate( self.lines ):
            if i < max( self.ip-10, 0 ): continue
            if i > min( self.lastLine+1, self.ip+10 ): continue
            line = self.lines[i]
            if i == self.ip:
                try:
                    line = line[0:9] + "-->" + line[12:]
                except:
                    print( "Error: line = ", line )
                    line = line
            print( "{0:3}: {1:1}".format(i, line ) )

    def __str__( self ):
        s = "Ip = " + str( self.ip  ) + "\n"
        s += "line[Ip] = " + str( self.lines[self.ip] ) + "\n"
        return s

    def containsInstr( self, line ):
        for instr in instructions:
            if line.lower().find( instr.lower()+" " ) != -1:
                return True
        return False

    def getDest( self, line ):
        line = line.split( ";" )[0].strip()
        dest = line.split()[-1]
        return dest

    def nextIp( self ):
        ip = self.ip
        while True:
            ip += 1
            line = self.lines[ip]
            
            if ip >= len( self.lines ):
                print( "\n\nException. IP running wild!\n\n" )
                sys.exit()
                
            if self.containsInstr( line ):
                return ip
            
    def step( self ):
        # execute the current line
        isJump = self.execute()

        if isJump:
            return
        
        # otherwise, advance ip to the next line
        # that contains an instruction
        line = self.lines[ self.ip ]
        self.ip = self.nextIp()

    def push( self, item, comment=None ):
        registers[ "esp" ] -= 1
        if comment==None:
            stack[ registers[ "esp" ] ] = item
        else:
            stack[ registers[ "esp" ] ] = \
                   "{0:1} {1:1}".format( item, comment )

    def pop( self ):
        item = stack[ registers[ "esp" ] ]
        if type( item )==type( "string" ):
            item = int( item.split()[0].strip() )
        registers[ "esp" ] += 1
        return item

        
    def execute( self ):
        line = self.lines[ self.ip ]
        
        # remove comments
        stackComment = None
        if line.find( ";" ) != -1:
            comment = line.split( ";" )[-1]
            line = line.split( ";" )[0]
            #print( "line = ", line )
            #print( "comment = ", comment )
            if comment.find( "TOS <--" ) != -1:
                stackComment = comment.split( "TOS <--" )[-1]
                #print( "Stack Comment = ", stackComment )

        line = line.lower()
        
        # remove label
        label = None
        if line.find( ":" ) != -1:
            label = line.split( ":" )[0].strip()
            line = line.split( ":" )[1]

        fields = line.split( )
        instruction = fields[0]
        if len( fields )==3:
            operand1 = fields[1].replace( ',','' ).strip()
            operand2 = fields[2].replace( ',','' ).strip()
        if len( fields )==2:
            operand1 = fields[-1].replace( ',','' ).strip()
            operand2 = None
        if len( fields )==1:
            operand1 = None
            operand2 = None
            
        #print( "label=", label, " inst=", instruction, " op1=", operand1,
        #       "op2=", operand2 )
        
        # int 0x80?
        if instruction == "int" and operand1 =="0x80":
            print( "\n\nDone!\n\n" )
            sys.exit()

        # call label?
        if instruction == "call":
            label = self.getDest( line )
            #print( "call to", label )
            self.push( self.nextIp(), "*" )
            self.ip = self.labelAddress[ label ]
            return True # is a jump

        # push
        if instruction == "push":
            if operand1 in registers.keys():
                self.push( registers[ operand1 ], stackComment )
            else:
                print( "*** Error: trying to push ", operand1 )
            return False

        # pop
        if instruction == "pop":
            if operand1 in registers.keys():
                data = self.pop()
                registers[ operand1 ] = data
            else:
                print( "*** Error: trying to pop into ", operand1 )
            return False

        # mov
        if instruction == "mov":
            #print( "mov: instr=", instruction, "op1=", operand1,
            #       "op2=", operand2 )
            if operand2 in registers.keys():                
                data = int( registers[operand2] )
            elif operand2.find( "ebp+" )!=-1:
                operand2 = operand2.replace( "]", "" )
                offset = int( operand2.split( "ebp+" )[1] )
                data = stack[ registers["ebp"] + offset//4 ]
                if type(data)==type("string"):
                    data = int( data.split()[0].strip() )
                #print( "mov operand2 = ", operand2, "offset = ", offset )
            else:
                data = int( operand2 )

            #print( "mov: data = ", data )
            
            if operand1 in registers.keys():
                registers[ operand1 ] = data
                #print( "mov: setting", operand1, "to", data )
            elif operand1.find( "ebp+" )!=-1:
                operand1 = operand1.replace( "]", "" )
                offset = int( operand1.split( "ebp+")[1] )
                stack[ registers["ebp"] + offset//4 ] = data
            return False
        
        # dec
        if instruction == "dec":
            if operand1 in registers.keys():
                registers[operand1] -= 1
            else:
                print( "*** Error *** trying to decrement", operand1 )
            return False

        # mul
        if instruction == "mul":
            if operand1 in registers.keys():
                registers["eax"] = registers[operand1] * registers["eax"]
                registers["edx"] = 0
            return False
                 
        # cmp
        if instruction == "cmp":
            if operand2 in registers.keys():
                data = int( registers[operand2] )
            elif operand2.find( "ebp+" )!=-1:
                operand2 = operand2.replace( "]", "" )
                offset = int( operand2.split( "ebp+" )[1] )
                data = stack[ registers["ebp"] + offset//4 ]
                if type(data)==type("string"):
                    data = int( data.split()[0].strip() )

            else:
                data = int( operand2 )
                            
            if operand1 in registers.keys():
                #print( "cmp: operand1 = ", operand1, "value = ",
                #       registers[ operand1 ] )
                flags["l"]  = registers[ operand1 ] < data
                flags["le"] = registers[ operand1 ] <= data
                flags["e"]  = registers[ operand1 ] == data
                flags["ne"] = registers[ operand1 ] != data
                flags["g"]  = registers[ operand1 ] > data
                flags["ge"] = registers[ operand1 ] >= data
            elif operand1.find( "ebp+" )!=-1:
                operand1 = operand1.replace( "]", "" )
                offset = int( operand1.split( "ebp+")[1] )
                operand1 = stack[ registers["ebp"] + offset//4 ]
                if type(operand1)==type("string"):
                    operand1 = int( operand1.split()[0].strip() )

                #print( "cmp: operand1 = ", operand1 )
                flags["l"]  = operand1 < data
                flags["le"] = operand1 <= data
                flags["e"]  = operand1 == data
                flags["ne"] = operand1 != data
                flags["g"]  = operand1 > data
                flags["ge"] = operand1 >= data

            print( "cmp: flags = ", flags )
            return False

        # jg
        if instruction == "jg":
            if flags["g"]:
                self.ip = self.labelAddress[ operand1 ]
                return True
            return False
        
        # ret?
        if instruction == "ret":
            self.ip = self.pop()

            # get the number after ret
            if operand1 != None:
                operand1 = int( operand1 )
                for i in range( 0, operand1, 4 ):
                    self.pop()
            return True
        
        return False

def displayStack():
    file = open( "stack.txt", "w" )
    file.write( "\n" )
    file.write( "+" + ("-"*18) + "+ \n" )
    for i in range( len(stack)-1, registers["esp"]-1, -1 ):
        file.write( "|" + "   {0:<12}   ".format( stack[i] ) + "|")
        if i==registers["esp"]:
            file.write( "<-- esp " )
        if i==registers["ebp"]:
            file.write( "<-- ebp " )
        file.write( "\n" ) 
    file.write( "+" + ("-"*18) + "+\n" )
    file.close()
    
def displayRegs():
    file = open( "registers.txt", "w" )
    for reg in ["eax", "ebx", "ecx", "edx" ]:
        file.write( reg.upper() + ": " +str( registers[reg] ) + "\n" )
    file.write( "\n" )
    file.close()
    
def main():
    # initialize the registers
    registers["esp"] = len( stack )-1

    # initialize the listing
    listing = Listing( program )

    # display the first listing
    os.system('clear')
    listing.display()

    # simulation loop.  Single step through the
    # program.
    while True:
        input( "> " )
        os.system('clear')
        listing.step()
        listing.display()
        displayStack()
        displayRegs()

main()


Factorial in Intel Assembly


;;;  -------------------------
;;;  code area
;;;  -------------------------
                section .text
                global  _start

;;; ; ---------------------------------------------------------
;;; ; main program
;;; ; ---------------------------------------------------------
_start:         mov     eax, 4  ; n = 4

                push    eax     ; TOS <-- XX
                push    eax     ; TOS <-- n
                call    fact
                pop     eax     ; get n in eax

;;;  return to OS
                mov     eax, 1
                mov     ebx, 0
                int     0x80

;;; ; ---------------------------------------------------------
fact:           push    ebp     ; TOS <-- old ebp
                mov     ebp, esp
                
;;;  test for stopping condition

                cmp     dword[ebp+8], 1    ; n==1 ?
                jg      .recurse
                mov     dword[ebp+12], 1 ; yes, then n! = 1
                pop     ebp     ; return to caller
                ret     4

;;;  recursive step

.recurse:       push    eax     ; TOS <-- old eax
                push    ebx     ; TOS <-- old ebx
                push    edx     ; TOS <-- old edx
        
                mov     eax, dword[ebp+8]  
                dec     eax     ; n-1 in eax
                push    eax     ; TOS <-- XX
                push    eax     ; TOS <-- n-1
                call    fact    ; recursive call
                pop     ebx     ; get fact(n-1) in ebx
                mov     eax, dword[ebp+8]
                mul     ebx     ; edx:eax <-- n * (n-1)!
        
                mov     dword[ebp+12], eax ; that's n!
                                ; drop edx
                pop     edx     ; restore regs
                pop     ebx
                pop     eax
        
                pop     ebp     ; return
                ret     4