Exemple #1
0
def while_(c, m):
    """While control flow statement. For instance:
        while bx < 4 {
            ; code
        }

        To force at least one iteration
        while dx > 7 or once {
            ; code
        }
    """
    a = Operand(c, m.group(1))
    comparision = m.group(2)
    b = Operand(c, m.group(3))

    label = c.get_uid(m.group(5))
    labelstart = label + '_s'
    labelend = label + '_e'

    if m.group(4) is None:
        # 'or once' is not present, we might not need to enter the loop
        c.add_code([f'cmp {a}, {b}', f'{ctoi[comparision]} {labelend}'])
    c.add_code(f'{labelstart}:')

    # Reenter the loop if condition is met
    c.add_pending_code(
        [f'cmp {a}, {b}', f'{cti[comparision]} {labelstart}', f'{labelend}:'])
Exemple #2
0
def modulo(c, m):
    """Modulo statement. For instance:
        al %= 6
    """
    dst = Operand(c, m.group(1))
    src = Operand(c, m.group(2))

    if src.value == 0:
        raise ValueError('Cannot perform modulo 0')

    if src.value < 0:
        raise ValueError('Negative modulo not implemented')

    if src.value is not None and is_power_of_2(src.value):
        # AND optimization: modulo is a power of 2
        # We only need to mask with 111… until its power.
        # For instance:
        #   1000 (8) - 1 = 111 (7)
        #
        # So n % 8, e.g., 100101, is as simple as masking with 111 -> 101
        mask = src.value - 1
        c.add_code(f'and {dst}, {mask}')
        return

    perform_divide(c, dst, src, result_in8='ah', result_in16='dx')
Exemple #3
0
def if_(c, m):
    """If control flow statement. For instance:
        if ax < 5 {
            ; code
        }

        if cx is even {
            ; code
        }
    """
    a = Operand(c, m.group(1))
    comparision = m.group(2)
    b = Operand(c, m.group(3))

    even_odd = m.group(4)
    label = c.get_uid(m.group(5))

    if even_odd is None:
        c.add_code([f'cmp {a}, {b}', f'{ctoi[comparision]} {label}'])
    else:
        # If even, then bit is 0, then jump to skip if not zero
        # If  odd, then bit is 1, then jump to skip if zero
        jump = 'jnz' if even_odd == 'even' else 'jz'
        c.add_code([f'test {a}, 1', f'{jump} {label}'])

    c.add_pending_code(f'{label}:')
def check_for_arrows(image, equation, ncp, ind):
    x0, y0, h0, w0, x, y, w, h, x1, y1, w1, h1 = get_coords_of_adjacent(
        ncp, ind)

    if math.fabs(x - x0) < 0.4 * w and math.fabs((x0 + w0) -
                                                 (x + w)) < 0.4 * w and y0 < y:
        obj = Operand(x=x,
                      y=y,
                      top=Operand(x=x0,
                                  y=y0,
                                  image=image[y0:y0 + h0, x0:x0 + w0]),
                      image=image[y:y + h, x:x + w])
        equation.add_operand(obj)
        return True
    elif math.fabs(x - x1) < 0.4 * w and math.fabs(
        (x1 + w1) - (x + w)) < 0.4 * w and y1 < y:
        obj = Operand(x=x,
                      y=y,
                      top=Operand(x=x1,
                                  y=y1,
                                  image=image[y1:y1 + h0, x1:x1 + w1]),
                      image=image[y:y + h, x:x + w])
        equation.add_operand(obj)
        return True
    else:
        equation.add_operand(Operand(x=x, y=y, image=image[y:y + h, x:x + w]))
        return False
Exemple #5
0
def divide(c, m):
    """Division statement. For instance:
        al /= 4
    """
    dst = Operand(c, m.group(1))
    src = Operand(c, m.group(2))


    if src.value == 0:
        raise ValueError('Cannot divide by 0')


    if src.value and abs(src.value) == 1:
        # One optimization: do nothing or negate
        if src.value < 0:
            c.add_code(f'neg {dst}')
        return


    if src.value is not None:
        # Bit shift optimization: divisor is a small multiple of 2
        # http://zsmith.co/intel_d.html#div or
        # http://zsmith.co/intel_n.html#neg and http://zsmith.co/intel_s.html#sar
        if dst.is_reg or dst.size == 8:
            limit = 80
        else:
            limit = 150

        if dst.is_reg:
            if src.value < 0:
                cost = 3 + 8 + 4 * (-src.value)
            else:
                cost = 8 + 4 * src.value

        if dst.is_mem:
            if src.value < 0:
                cost = 16 + 20 + 4 * (-src.value)
            else:
                cost = 20 + 4 * src.value

        # Now we know our limit, beyond which division is easier
        # and the cost per shift, so we know which values we can use
        if cost < limit:
            # The cost of negating and shifting is less, so we'll do that
            amount = limit // cost
            powers = [2**i for i in range(1, amount + 1)]
            if abs(src.value) in powers:
                # We have a power of two, so perform we can use bit shift
                if src.value < 0:
                    c.add_code(f'neg {dst}')
                    src.value = -src.value

                count = int(src.value ** 0.5)
                c.add_code(f'sar {dst}, {count}')
                return

    perform_divide(c, dst, src,
                   result_in8='al',
                   result_in16='ax')
Exemple #6
0
    def get_operands(self, csv):
        """Converts the possibly comma separated values to a
            list of operands (inmediate, registers and variables
        """
        if isinstance(csv, list):
            return [
                v if isinstance(v, Operand) else Operand(self, v) for v in csv
            ]

        if isinstance(csv, Operand):
            return [csv]

        return [Operand(self, v) for v in Operand.get_csv(csv)]
 def __init__(self,
              inputPipe=None,
              config={},
              simulation=False,
              dataManager=None):
     self.traders = []
     self.config = config
     self.input = inputPipe
     self.simulation = simulation
     self.zero = Operand(d=0)
     self.one = Operand(d=1)
     self.dataManager = dataManager
     self.initialize()
 def get_operation(self, arg):
     if (type(arg) is str):
         #it is an operation
         operation = create_operation(data=json.loads(arg),
                                      dataline=self.dataManager)
         return operation
     elif (type(arg) is float or type(arg) is int):
         #return the integer or float as it is
         return Operand(d=arg)
     elif (type(arg) is list):
         return Operand(d=0)
     elif (arg is None):
         #return 0
         return Operand(d=0)
Exemple #9
0
def dualoperands(c, m):
    """Operations with two operands statement. For instance:
        ax |= bx  ; Bitwise OR
        dx ^= cx  ; Bitwise XOR
        ax &= bx  ; Bitwise AND
        dx += cx  ; Integer addition
        ax -= bx  ; Integer subtraction
    """
    op = m.group(2)
    dst = Operand(c, m.group(1))
    src = Operand(c, m.group(3))

    if src.value == 1 and op in translation1:
        c.add_code(f'{translation1[op]} {dst}')
    else:
        helperoperate(c, translation[op], dst, src)
 def run(self, data):
     d = {
         'price': data['lastPrice'],
         'time': data['time'],
         'token': data['token'],
         'lastPrice': data['lastPrice']  # TODO change to common as price
     }
     if (data.get('isCandle')):
         d.update({
             'open': data['open'],
             'high': data['high'],
             'low': data['low'],
             'close': data['close'],
             'interval': data['interval'],
             'isCandle': data['isCandle']
         })
     #TODO change this as soon as you can !important
     true = Operand(d=True)
     begin = (self.begin_on.update(d) == true).d
     end = (self.end_on.update(d) == true).d
     r = self.update(d)
     if (begin and not end):
         if (self.phase == 'sleeping'):
             self.begin(d)
             self.phase = 'running'
         if (data.get('noTrade')):
             return None
         return r
     if (begin and end and self.phase == 'running'):
         self.end(d)
         self.phase = 'sleeping'
     return None
Exemple #11
0
def repeat(c, m):
    """Repeat control flow statement. For instance:
        repeat 10 with cx {
            ; code
        }
    """
    count = Operand(c, m.group(1))
    used = Operand(c, m.group(2))
    label = c.get_uid(m.group(3))
    labelstart = label + '_s'
    labelend = label + '_e'

    # Initialize our loop counter
    helperassign(c, used, count)

    # Sanity check, if 0 don't enter the loop unless we know it's not 0
    # This won't optimize away the case where the value is 0
    if count.value is None or count.value <= 0:  # count unknown or zero
        c.add_code([
            f'test {used}, {used}',
            f'jz {labelend}'
        ])

    # Add the start label so we can jump here
    c.add_code(f'{labelstart}:')

    if used.code == 'cx':
        # We can use 'loop' if using 'cx'
        c.add_pending_code([
            f'loop {labelstart}',
            f'{labelend}:'
        ])
    else:
        c.add_pending_code([
            f'dec {used}',
            f'jnz {labelstart}',
            f'{labelend}:'
        ])
Exemple #12
0
def putchar(c, m):
    """Puts a character on the screen. For instance:
        put char 'L'
        put digit cx
    """
    # TODO Possibly add support for not inlining
    src = Operand(c, m.group(2))

    c.add_code('push ax')

    helperassign(c, 'al' if src.size == 8 else 'ax', src)
    if m.group(1) == 'digit':
        c.add_code("add al, '0'")

    c.add_code([
        'mov ah, 14',
        'int 10h',
        'pop ax'
    ])
Exemple #13
0
def multiply(c, m):
    """Multiplication statement. For instance:
        al *= 7
    """
    dst = Operand(c, m.group(1))
    src = Operand(c, m.group(2))

    if src.value == 0:
        # Zero optimization: second product is 0
        if dst.is_mem:
            c.add_code(f'and {dst}, 0')
        else:
            c.add_code(f'xor {dst}, {dst}')
        return

    if src.value and abs(src.value) == 1:
        # One optimization: do nothing or negate
        if src.value < 0:
            c.add_code(f'neg {dst}')
        return

    if src.value is not None:
        # Bit shift optimization: second product is a small multiple of 2
        # http://zsmith.co/intel_m.html#mul or
        # http://zsmith.co/intel_n.html#neg and http://zsmith.co/intel_s.html#sal
        if dst.is_reg or dst.size == 8:
            limit = 70
        else:
            limit = 124

        if dst.is_reg:
            if src.value < 0:
                cost = 3 + 8 + 4 * (-src.value)
            else:
                cost = 8 + 4 * src.value

        if dst.is_mem:
            if src.value < 0:
                cost = 16 + 20 + 4 * (-src.value)
            else:
                cost = 20 + 4 * src.value

        # Now we know our limit, beyond which multiplication is easier
        # and the cost per shift, so we know which values we can use
        if cost < limit:
            # The cost of negating and shifting is less, so we'll do that
            amount = limit // cost
            powers = [2**i for i in range(1, amount + 1)]
            if abs(src.value) in powers:
                # We have a power of two, so perform we can use bit shift
                if src.value < 0:
                    c.add_code(f'neg {dst}')
                    src.value = -src.value

                count = int(src.value**0.5)
                c.add_code(f'sal {dst}, {count}')
                return

    # We're likely going to need to save some temporary variables
    # No 'push' or 'pop' are used because 'ah *= 3', for instance, destroys 'al'
    # 'al' itself can be pushed to the stack, but a temporary variable can hold
    tmps = TmpVariables(c)

    # We will define 'dst, src, fa1, fa2' as:
    #   dst *= src
    #   fa1 *= fa2

    large_multiply = max(dst.size, src.size) > 8

    if large_multiply:
        # 16-bits mode, so we use 'ax' as the first factor
        fa1 = 'ax'

        # The second factor cannot be an inmediate value neither 'ax'
        if src.code[0] == 'a' and src.code[-1] in 'xhl' \
                or src.value is not None:
            fa2 = 'dx'
        else:
            fa2 = src.code

        # Determine which registers we need to save
        saves = []

        # al    *= 260
        # ^ dst
        #
        # ax    *= 260
        # ^ used
        #
        # dst ≠ used, used gets saved

        # Special case where we want to use al/ah as destination
        # We're using ax for multiplication, so we can't save the whole
        if dst[0] == 'a' and dst[-1] in 'hl':
            saves.append('al' if dst.code == 'ah' else 'ah')
        elif dst.code != fa1:
            # Save fa1 unless it's the destination
            saves.append(fa1)

        # Save fa2 unless it's the destination
        if dst.code != fa2:
            saves.append(fa2)

        # Save the used registers
        tmps.save_all(saves)

        # Load the factors into their correct location
        helperassign(c, [fa1, fa2], [dst, src])

        # Perform the multiplication
        c.add_code(f'mul {fa2}')

        # Move the result, 'ax', to wherever it should be
        helperassign(c, dst, 'ax')

        # Restore the used registers
        tmps.restore_all()

    else:
        # 8-bits mode, so we use 'al' as the first factor
        fa1 = 'al'

        # The second factor cannot be an inmediate value neither 'al'
        fa2 = src.code if src.value is None and src.code != 'al' else 'ah'

        # Determine which registers we need to save
        saves = []

        # Save fa1 unless it's the destination
        if dst.code != fa1:
            saves.append(fa1)

        # Save fa2 if we moved it (thus it's not the same)
        # unless it's be overrode if it is the destination
        if src.code != fa2 and dst.code != fa2:
            saves.append(fa2)

        # Save the used registers
        tmps.save_all(saves)

        # Load the factors into their correct location
        helperassign(c, [fa1, fa2], [dst, src])

        # Perform the multiplication
        c.add_code(f'mul {fa2}')

        # Move the result, 'al', to wherever it should be
        helperassign(c, dst, 'al')

        # Restore the used registers
        tmps.restore_all()
Exemple #14
0
def printf(c, m):
    """Prints a string with optional formatted values. For instance:
        printf "I'm %d years old, you %d" % ax, bx
        printf stringVariable
    """
    # TODO Possibly add support for not inlining
    #      Inlining really shines when there are actually formatted values:
    #
    #      If inlining:
    #           5 instructions; push/pop ax/dx, mov ah 9
    #        + 2n (where n is the number of strings, lea + int)
    #
    #      If not inlining (the first 7 are only written once, ran many times):
    #          7n instructions; function definition, push/pop ax, mov ah 9, int
    #        +  2 instructions; push/pop dx
    #        + 2n (where n is the number of strings, lea + int)
    #
    # Retrieve the captured values
    string = m.group(1)
    args = c.get_operands(m.group(2))
    var = m.group(3)

    # Save the registers used for calling the print interruption
    c.add_code(['push ax', 'push dx'])

    # string/args and var are mutually exclusive
    if var:
        op = Operand(c, var, assert_vector_access=False)
        if op.is_mem:
            # Using a variable, load and call the interruption
            if op.type == 'string':
                c.add_code(f'lea dx, {op}')
            else:
                load_integer(c, op.code)

            c.add_code([f'mov ah, 9', f'int 21h'])

        elif op.is_reg:
            # Using a register, which means we need to load an integer
            load_integer(c, op)
            c.add_code([f'mov ah, 9', f'int 21h'])

        else:
            # Assume inmediate integer value
            c.add_code(f'mov ah, 9')
            define_and_print(c, f'"{op.name}"')

    else:
        # Captured a string, with possibly formatted arguments
        #
        # Since 'ax' and 'dx' are needed for printing, we need to save them.
        # We could use the stack, however, we would need to push as many times
        # as we encountered one of the offending registers. Also, the stack
        # doesn't support the 8-bit versions of these registers, so we simply
        # use a temporary variable.
        tmps = TmpVariables(c)

        for a in args:
            if is_ax_or_dx_variant(a):
                tmps.save(a)

        # After we saved the values we may later need, set up the print function
        c.add_code('mov ah, 9')

        # Now, we have the right string and the arguments used
        if args:
            # 1. Find %X's
            substrings = re.split(r'%[sd]', string)
            arg_types = re.findall(r'%[sd]', string)

            # 2. Print: substring, formatted, substring, formatted, etc.
            for i in range(len(substrings)):
                if i > 0:
                    # Format the arguments, skipping 1 normal string (thus -1)
                    op = args[i - 1]
                    format_type = arg_types[i - 1]

                    # Load the argument into 'dx' depending on its type
                    #
                    # This assumes that the user entered the right type
                    # TODO Don't assume that the user entered the right type
                    if format_type == '%s':
                        c.add_code(f'lea dx, {op}')
                    elif format_type == '%d':
                        if op in tmps:
                            # Special, this was saved previously
                            load_integer(c, tmps[op])
                            c.add_code('mov ah, 9')
                        else:
                            load_integer(c, op)
                    else:
                        raise ValueError(
                            f'Regex should not have allowed {format_type}')

                    # Call the interruption to print the argument
                    c.add_code(f'int 21h')

                # Argument formatted, print the next part of the string
                define_and_print(c, substrings[i])
        else:
            # No arguments at all, a define and load will do
            define_and_print(c, string)

    # All done, restore the original dx/ax values
    c.add_code(['pop dx', 'pop ax'])
Exemple #15
0
def helperoperate(c, op, dst, src):
    """Helper operation with support to operate on any value
        (memory or not) of the form 'instruction dst, src'
    """
    if not isinstance(dst, Operand):
        dst = Operand(c, dst)

    if not isinstance(src, Operand):
        src = Operand(c, src)

    # Sanity check: destination cannot be an inmediate value
    if dst.value is not None:
        raise ValueError(
            f'Cannot {op} "{src.name}" to the inmediate value "{dst.name}"')

    # If the source has an integer value, make sure it fits on destination
    if src.value is not None:
        if src.size > dst.size:
            raise ValueError(f'"{src.name}" is too big to fit in "{dst.name}"')

        # It's an okay inmediate value, simply operate and early exit
        c.add_code(f'{op} {dst}, {src}')
        return

    # Special case where both are memory, we need to use a temporary
    # register ('ax' for instance); recursive calls to helperoperate()
    # will then take care of the cases where operating with memory + 'ax'
    if dst.is_mem and src.is_mem:
        # # # [Case memory to memory]
        # TODO Perhaps we could use a temporary variable instead of the
        #      stack to pick either AL or AX (which is better suited)
        c.add_code('push ax')
        helperassign(c, 'ax', src)
        helperoperate(c, op, dst, 'ax')
        c.add_code('pop ax')
        return

    # Now that we know the size, and that not both are memory,
    # we can get our hands dirty
    if dst.size == src.size:
        # # # [Case same size]
        # Both sizes are the same, we can directly operate (unless 'mov')
        if op != 'mov' or dst != src:
            c.add_code(f'{op} {dst}, {src}')

    elif dst.size < src.size:
        # The destination is smaller, we have to ignore the high part
        if src.is_mem or src.low is None:
            # # # [Case large memory/register to small register]
            # The source is memory, then the destination is a register
            #
            # We need an auxiliary register because we don't want to
            # touch the other part (either of r'[HL]'), and we can't
            # pop a masked value not to lose the rest
            #
            # This is also the case when the source is not memory, but
            # the register doesn't support accessing r'[HL]'
            #
            # Use either 'dx' or 'ax' as auxiliar register (arbitrary
            # as long as it doesn't match the one we want to move to)
            aux = 'dx' if dst[0] == 'a' else 'ax'
            c.add_code(f'push {aux}')
            helperassign(c, aux, src)
            helperoperate(c, op, dst, f'{aux[0]}l')
            c.add_code(f'pop {aux}')
        else:
            # # # [Case large register to small register/memory]
            # We know that we have acces to the low part since it was
            # checked above, otherwise we would have needed that
            # auxiliary to be able to pick only the low part
            helperoperate(c, op, dst, src.low)

    else:
        # The destination is larger, we need to mask the high part.
        # The only case where this is possible is on r'[ABCD][HL]',
        # so we already know that we have access to the 'X' version
        # unless the source is memory
        if src.is_mem:
            # # # [Case small memory to large register]
            # Check if the register supports accessing to the r'[HL]'
            # Otherwise we need to use a temporary register such as 'ax'
            if dst.low is None:
                c.add_code(f'push ax')
                c.add_code(f'xor ah, ah')
                helperassign(c, 'al', src)
                helperoperate(c, op, dst, 'ax')
                c.add_code(f'pop ax')
            else:
                c.add_code(f'xor {dst.high}, {dst.high}')
                helperoperate(c, op, dst.low, src)
        elif dst.is_mem:
            # # # [Case small register to large memory]
            # All the 8 bit registers support accessing to 'X', so we
            # can just mask away the high part, move it and restore
            c.add_code(f'push {src.full}')

            # We might need to move the high to the low part before masking
            if src[-1] == 'h':
                helperassign(c, src.low, src.high)
            c.add_code(f'xor {src.high}, {src.high}')

            helperoperate(c, op, dst, src.full)
            c.add_code(f'pop {src.full}')
        else:
            # # # [Case small register to large register]
            if src[-1] == 'l':
                # We're using the low part, we can directly copy
                # and then mask the high part with AND or XOR
                helperoperate(c, op, dst, src.full)
                if dst.high is None:
                    c.add_code(f'and {dst}, 0xff')
                else:
                    c.add_code(f'xor {dst.high}, {dst.high}')
            else:
                # We want to assign the src = YH, but it's 8-bits
                if dst.low is None:
                    # No support to move the 8-bits directly, we need
                    # to save the value, move it, mask it, and move it
                    c.add_code(f'push {src.full}')
                    helperassign(c, src.low, src)
                    c.add_code(f'xor {src}, {src}')
                    helperoperate(c, op, dst, src.full)
                    c.add_code(f'pop {src.full}')
                else:
                    # Destination supports 8-bits access so we can
                    # directly move those and mask away the high part
                    helperoperate(c, op, dst.low, src)
                    c.add_code(f'xor {dst.high}, {dst.high}')