Exemple #1
0
 def open(self, number, param, filetype, mode, access, lock,
                    reclen, seg, offset, length):
     """ Open a file on COMn: """
     if not self.stream:
         raise error.RunError(error.DEVICE_UNAVAILABLE)
     # PE setting not implemented
     speed, parity, bytesize, stop, rs, cs, ds, cd, lf, _ = self.get_params(param)
     # open the COM port
     if self.stream.is_open:
         raise error.RunError(error.FILE_ALREADY_OPEN)
     else:
         try:
             self.stream.open(rs, cs, ds, cd)
         except EnvironmentError as e:
             # device timeout
             logging.debug("Serial exception: %s", e)
             raise error.RunError(error.DEVICE_TIMEOUT)
     try:
         self.stream.baudrate = speed
         self.stream.parity = parity
         self.stream.bytesize = bytesize
         self.stream.stopbits = stop
     except Exception:
         self.stream.close()
         raise
     f = COMFile(self.stream, lf)
     # inherit width settings from device file
     f.width = self.device_file.width
     f.col = self.device_file.col
     return f
Exemple #2
0
def open_diskfile(fhandle, filetype, mode, name='', number=0, access='RW', lock='',
                  reclen=128, seg=0, offset=0, length=0):
    """ Create disk file object of requested type. """
    # determine file type if needed
    if len(filetype) > 1 and mode == 'I':
        # read magic
        first = fhandle.read(1)
        fhandle.seek(-1, 1)
        try:
            filetype_found = devices.magic_to_type[first]
            if filetype_found not in filetype:
                raise error.RunError(error.BAD_FILE_MODE)
            filetype = filetype_found
        except KeyError:
            filetype = 'A'
    if filetype in 'BPM':
        # binary [B]LOAD, [B]SAVE
        return BinaryFile(fhandle, filetype, number, name, mode,
                           seg, offset, length)
    elif filetype == 'A':
        # ascii program file (UTF8 or universal newline if option given)
        return TextFile(fhandle, filetype, number, name, mode, access, lock,
                         utf8_files, universal_newline, split_long_lines=False)
    elif filetype == 'D':
        if mode in 'IAO':
            # text data
            return TextFile(fhandle, filetype, number, name, mode, access, lock)
        else:
            return RandomFile(fhandle, number, name, access, lock, reclen)
    else:
        # internal error - incorrect file type requested
        logging.debug('Incorrect file type %s requested for mode %s',
                      filetype, mode)
        raise error.RunError(error.INTERNAL_ERROR)
Exemple #3
0
 def get(self, lcoord0, lcoord1, array_name):
     """ Read a sprite from the screen (GET). """
     x0, y0 = self.view_coords(*self.get_window_physical(*lcoord0))
     x1, y1 = self.view_coords(*self.get_window_physical(*lcoord1))
     self.last_point = x1, y1
     try:
         _, byte_array, version = state.basic_state.arrays[array_name]
     except KeyError:
         raise error.RunError(error.IFC)
     dx, dy = x1 - x0 + 1, y1 - y0 + 1
     # Tandy screen 6 simply GETs twice the width, it seems
     if self.screen.mode.name == '640x200x4':
         x1 = x0 + 2 * dx - 1
     # illegal fn call if outside viewport boundary
     vx0, vy0, vx1, vy1 = self.get_view()
     util.range_check(vx0, vx1, x0, x1)
     util.range_check(vy0, vy1, y0, y1)
     # set size record
     byte_array[0:4] = self.screen.mode.sprite_size_to_record(dx, dy)
     # read from screen and convert to byte array
     sprite = self.screen.get_rect(x0, y0, x1, y1)
     try:
         self.screen.mode.sprite_to_array(sprite, dx, dy, byte_array, 4)
     except ValueError:
         raise error.RunError(error.IFC)
     # store a copy in the sprite store
     self.sprites[array_name] = (dx, dy, sprite, version)
Exemple #4
0
def value_point(ins):
    """ POINT: get pixel attribute at screen location. """
    util.require_read(ins, ('(', ))
    lst = parse_expr_list(ins, 2, err=error.STX)
    util.require_read(ins, (')', ))
    if not lst[0]:
        raise error.RunError(error.STX)
    screen = state.console_state.screen
    if not lst[1]:
        # single-argument version
        try:
            x, y = screen.drawing.last_point
            fn = vartypes.pass_int_unpack(lst[0])
            if fn == 0:
                return vartypes.pack_int(x)
            elif fn == 1:
                return vartypes.pack_int(y)
            elif fn == 2:
                fx, _ = screen.drawing.get_window_logical(x, y)
                return fp.pack(fx)
            elif fn == 3:
                _, fy = screen.drawing.get_window_logical(x, y)
                return fp.pack(fy)
        except AttributeError:
            return vartypes.null['%']
    else:
        # two-argument mode
        if screen.mode.is_text_mode:
            raise error.RunError(error.IFC)
        return vartypes.pack_int(
            screen.drawing.point(
                (fp.unpack(vartypes.pass_single_keep(lst[0])),
                 fp.unpack(vartypes.pass_single_keep(lst[1])), False)))
Exemple #5
0
def value_fn(ins):
    """ FN: get value of user-defined function. """
    fnname = util.get_var_name(ins)
    try:
        varnames, fncode = state.basic_state.functions[fnname]
    except KeyError:
        raise error.RunError(error.UNDEFINED_USER_FUNCTION)
    # save existing vars
    varsave = {}
    for name in varnames:
        if name in state.basic_state.variables:
            # copy the *value* - set_var is in-place it's safe for FOR loops
            varsave[name] = state.basic_state.variables[name][:]
    # read variables
    if util.skip_white_read_if(ins, ('(', )):
        exprs = parse_expr_list(ins, len(varnames), err=error.STX)
        if None in exprs:
            raise error.RunError(error.STX)
        for i in range(len(varnames)):
            var.set_var(varnames[i], exprs[i])
        util.require_read(ins, (')', ))
    # execute the code
    fns = StringIO(fncode)
    fns.seek(0)
    value = parse_expression(fns)
    # restore existing vars
    for name in varsave:
        # re-assign the stored value
        state.basic_state.variables[name][:] = varsave[name]
    return value
Exemple #6
0
def value_to_str_keep(inp,
                      screen=False,
                      write=False,
                      allow_empty_expression=False):
    """ Convert BASIC number to BASIC string. """
    # screen=False means in a program listing
    # screen=True is used for screen, str$ and sequential files
    if not inp:
        if allow_empty_expression:
            return ('$', '')
        else:
            raise error.RunError(error.STX)
    typechar = inp[0]
    if typechar == '$':
        return ('$', inp[1])
    elif typechar == '%':
        if screen and not write and vartypes.unpack_int(inp) >= 0:
            return ('$', ' ' + str(vartypes.unpack_int(inp)))
        else:
            return ('$', str(vartypes.unpack_int(inp)))
    elif typechar == '!':
        return ('$', float_to_str(fp.unpack(inp), screen, write))
    elif typechar == '#':
        return ('$', float_to_str(fp.unpack(inp), screen, write))
    else:
        raise error.RunError(error.STX)
Exemple #7
0
def set_time(timestr):
    """ Set the system time offset. """
    now = datetime.datetime.now() + state.basic_state.time_offset
    timelist = [0, 0, 0]
    pos, listpos, word = 0, 0, ''
    while pos < len(timestr):
        if listpos > 2:
            break
        c = chr(timestr[pos])
        if c in (':', '.'):
            timelist[listpos] = int(word)
            listpos += 1
            word = ''
        elif (c < '0' or c > '9'):
            raise error.RunError(error.IFC)
        else:
            word += c
        pos += 1
    if word:
        timelist[listpos] = int(word)
    if timelist[0] > 23 or timelist[1] > 59 or timelist[2] > 59:
        raise error.RunError(error.IFC)
    newtime = datetime.datetime(now.year, now.month, now.day, timelist[0],
                                timelist[1], timelist[2], now.microsecond)
    state.basic_state.time_offset += newtime - now
Exemple #8
0
def set_field_var_or_array(random_file, varname, indices, offset, length):
    """ Attach a string variable to a FIELD buffer. """
    if varname[-1] != '$':
        # type mismatch
        raise error.RunError(error.TYPE_MISMATCH)
    if offset + length > len(random_file.field.buffer):
        # FIELD overflow
        raise error.RunError(error.FIELD_OVERFLOW)
    str_addr = random_file.field.address + offset
    str_sequence = bytearray(chr(length)) + vartypes.value_to_uint(str_addr)
    # assign the string ptr to the variable name
    # desired side effect: if we re-assign this string variable through LET, it's no longer connected to the FIELD.
    if indices == []:
        state.basic_state.variables[varname] = str_sequence
        # update memory model (see set_var)
        if varname not in state.basic_state.var_memory:
            name_ptr = state.basic_state.var_current
            var_ptr = name_ptr + max(
                3, len(varname)
            ) + 1  # byte_size first_letter second_letter_or_nul remaining_length_or_nul
            state.basic_state.var_current += max(
                3, len(varname)) + 1 + byte_size['$']
            state.basic_state.var_memory[varname] = (name_ptr, var_ptr)
    else:
        check_dim_array(varname, indices)
        dimensions, lst, _ = state.basic_state.arrays[varname]
        bigindex = index_array(indices, dimensions)
        lst[bigindex * 3:(bigindex + 1) * 3] = str_sequence
Exemple #9
0
def swap(name1, index1, name2, index2):
    """ Swap two variables by reference (Strings) or value (everything else). """
    if name1[-1] != name2[-1]:
        # type mismatch
        raise error.RunError(error.TYPE_MISMATCH)
    elif ((index1 == [] and name1 not in state.basic_state.variables) or
            (index1 != [] and name1 not in state.basic_state.arrays) or
            (index2 == [] and name2 not in state.basic_state.variables) or
            (index2 != [] and name2 not in state.basic_state.arrays)):
        # illegal function call
        raise error.RunError(error.IFC)
    typechar = name1[-1]
    size = vartypes.byte_size[typechar]
    # get buffers (numeric representation or string pointer)
    if index1 == []:
        p1, off1 = state.basic_state.variables[name1], 0
    else:
        dimensions, p1, _ = state.basic_state.arrays[name1]
        off1 = index_array(index1, dimensions)*size
    if index2 == []:
        p2, off2 = state.basic_state.variables[name2], 0
    else:
        dimensions, p2, _ = state.basic_state.arrays[name2]
        off2 = index_array(index2, dimensions)*size
    # swap the contents
    p1[off1:off1+size], p2[off2:off2+size] =  p2[off2:off2+size], p1[off1:off1+size]
    # inc version
    if name1 in state.basic_state.arrays:
        state.basic_state.arrays[name1][2] += 1
    if name2 in state.basic_state.arrays:
        state.basic_state.arrays[name2][2] += 1
Exemple #10
0
 def open(self, number, param, filetype, mode, access, lock, reclen, seg,
          offset, length):
     """ Open a file on tape. """
     if not self.tapestream:
         raise error.RunError(error.DEVICE_UNAVAILABLE)
     if self.tapestream.is_open:
         raise error.RunError(error.FILE_ALREADY_OPEN)
     if set(param) & self._illegal_chars:
         # Cassette BASIC throws bad file NUMBER, for some reason.
         raise error.RunError(error.BAD_FILE_NUMBER)
     try:
         if mode == 'O':
             self.tapestream.open_write(param, filetype, seg, offset,
                                        length)
         elif mode == 'I':
             _, filetype, seg, offset, length = self._search(
                 param, filetype)
         else:
             raise error.RunError(error.BAD_FILE_MODE)
     except EnvironmentError:
         raise error.RunError(error.DEVICE_IO_ERROR)
     if filetype == 'D':
         return CASTextFile(self.tapestream, filetype, mode)
     elif filetype == 'A':
         return CASTextFile(self.tapestream,
                            filetype,
                            mode,
                            split_long_lines=False)
     else:
         return CASBinaryFile(self.tapestream, filetype, mode, seg, offset,
                              length)
Exemple #11
0
def ml_parse_value(gmls, default=None):
    """ Parse a value in a macro-language string. """
    c = util.skip(gmls, ml_whitepace)
    sgn = -1 if c == '-' else 1
    if not c:
        raise error.RunError(error.IFC)
    if c in ('+', '-'):
        gmls.read(1)
        c = util.peek(gmls)
        # don't allow default if sign is given
        default = None
    if c == '=':
        gmls.read(1)
        c = util.peek(gmls)
        if len(c) == 0:
            raise error.RunError(error.IFC)
        elif ord(c) > 8:
            name = util.get_var_name(gmls)
            indices = ml_parse_indices(gmls)
            step = var.get_var_or_array(name, indices)
            util.require_read(gmls, (';',), err=error.IFC)
        else:
            # varptr$
            step = get_value_for_varptrstr(gmls.read(3))
    elif c in representation.ascii_digits:
        step = ml_parse_const(gmls)
    elif default is not None:
        step = default
    else:
        raise error.RunError(error.IFC)
    if sgn == -1:
        step = vartypes.number_neg(step)
    return step
Exemple #12
0
def dim_array(name, dimensions):
    """ Allocate array space for an array of given dimensioned size. Raise errors if duplicate name or illegal index value. """
    if state.basic_state.array_base is None:
        state.basic_state.array_base = 0
    name = vartypes.complete_name(name)
    if name in state.basic_state.arrays:
        raise error.RunError(error.DUPLICATE_DEFINITION)
    for d in dimensions:
        if d < 0:
            raise error.RunError(error.IFC)
        elif d < state.basic_state.array_base:
            raise error.RunError(error.SUBSCRIPT_OUT_OF_RANGE)
    size = array_len(dimensions)
    # update memory model
    # first two bytes: chars of name or 0 if name is one byte long
    name_ptr = state.basic_state.array_current
    record_len = 1 + max(3, len(name)) + 3 + 2*len(dimensions)
    array_ptr = name_ptr + record_len
    array_bytes = size*var_size_bytes(name)
    check_free_memory(record_len + array_bytes, error.OUT_OF_MEMORY)
    state.basic_state.array_current += record_len + array_bytes
    state.basic_state.array_memory[name] = (name_ptr, array_ptr)
    try:
        state.basic_state.arrays[name] = [ dimensions, bytearray(array_bytes), 0 ]
    except OverflowError:
        # out of memory
        raise error.RunError(error.OUT_OF_MEMORY)
    except MemoryError:
        # out of memory
        raise error.RunError(error.OUT_OF_MEMORY)
Exemple #13
0
def get_file(num, mode='IOAR'):
    """ Get the file object for a file number and check allowed mode. """
    try:
        the_file = state.io_state.files[num]
    except KeyError:
        raise error.RunError(error.BAD_FILE_NUMBER)
    if the_file.mode.upper() not in mode:
        raise error.RunError(error.BAD_FILE_MODE)
    return the_file
Exemple #14
0
 def open(self, number, param, filetype, mode, access, lock, reclen, seg,
          offset, length):
     """ Open a file on the device. """
     if not self.device_file:
         raise error.RunError(error.DEVICE_UNAVAILABLE)
     if mode not in self.allowed_modes:
         raise error.RunError(error.BAD_FILE_MODE)
     new_file = self.device_file.clone(filetype, mode, reclen)
     return new_file
Exemple #15
0
def base_array(base):
    """ Set the array base to 0 or 1 (OPTION BASE). Raise error if already set. """
    if base not in (1, 0):
        # syntax error
        raise error.RunError(error.STX)
    if state.basic_state.array_base is not None and base != state.basic_state.array_base:
        # duplicate definition
        raise error.RunError(error.DUPLICATE_DEFINITION)
    state.basic_state.array_base = base
Exemple #16
0
    def files(self, pathmask):
        """ Write directory listing to console. """
        # forward slashes - file not found
        # GW-BASIC sometimes allows leading or trailing slashes
        # and then does weird things I don't understand.
        if b'/' in bytes(pathmask):
            raise error.RunError(error.FILE_NOT_FOUND)
        if not self.path:
            # undefined disk drive: file not found
            raise error.RunError(error.FILE_NOT_FOUND)
        drivepath, relpath, mask = self._native_path_elements(
            pathmask, path_err=error.FILE_NOT_FOUND)
        path = os.path.join(drivepath, relpath)

        mask = mask.upper() or b'*.*'
        # output working dir in DOS format
        # NOTE: this is always the current dir, not the one being listed
        dir_elems = [
            join_dosname(*short_name(path, e)) for e in self.cwd.split(os.sep)
        ]
        console.write_line(self.letter + b':\\' + b'\\'.join(dir_elems))
        fils = []
        if mask == b'.':
            dirs = [split_dosname((os.sep + relpath).split(os.sep)[-1:][0])]
        elif mask == b'..':
            dirs = [split_dosname((os.sep + relpath).split(os.sep)[-2:][0])]
        else:
            all_names = safe(os.listdir, path)
            dirs = [
                filename_from_unicode(n) for n in all_names
                if os.path.isdir(os.path.join(path, n))
            ]
            fils = [
                filename_from_unicode(n) for n in all_names
                if not os.path.isdir(os.path.join(path, n))
            ]
            # filter according to mask
            dirs = filter_names(path, dirs + [b'.', b'..'], mask)
            fils = filter_names(path, fils, mask)
        if not dirs and not fils:
            raise error.RunError(error.FILE_NOT_FOUND)
        # format and print contents
        output = ([(b'%-8s.%-3s' % (t, e) if
                    (e or not t) else b'%-8s    ' % t) + b'<DIR>'
                   for t, e in dirs] +
                  [(b'%-8s.%-3s' % (t, e) if e else b'%-8s    ' % t) + b'     '
                   for t, e in fils])
        num = state.console_state.screen.mode.width // 20
        while len(output) > 0:
            line = b' '.join(output[:num])
            output = output[num:]
            console.write_line(line)
            # allow to break during dir listing & show names flowing on screen
            backend.check_events()
        console.write_line(b' %d Bytes free' % self.get_free())
Exemple #17
0
def pass_string(inp, allow_empty=False, err=error.TYPE_MISMATCH):
    """ Check if variable is String-valued. """
    if not inp:
        if not allow_empty:
            raise error.RunError(error.STX)
        else:
            return null('$')
    if inp[0] == '$':
        return inp
    else:
        raise error.RunError(err)
Exemple #18
0
def string_assign_into(name, indices, offset, num, value):
    """ Write a packed value into a string variable or array. """
    # WARNING - need to decrement basic offset by 1 to get python offset
    if value[0] != '$':
        # type mismatch
        raise error.RunError(error.TYPE_MISMATCH)
    s = vartypes.unpack_string(value)
    v = get_var_or_array_string_pointer(name, indices)
    if v is None:
        # illegal function call
        raise error.RunError(error.IFC)
    string_assign_unpacked_into(v, offset, num, s)
Exemple #19
0
 def _native_path_elements(self,
                           path_without_drive,
                           path_err,
                           join_name=False):
     """ Return elements of the native path for a given BASIC path. """
     path_without_drive = state.console_state.codepage.str_to_unicode(
         bytes(path_without_drive), box_protect=False)
     if u'/' in path_without_drive:
         # bad file number - this is what GW produces here
         raise error.RunError(error.BAD_FILE_NUMBER)
     if not self.path:
         # this drive letter is not available (not mounted)
         raise error.RunError(error.PATH_NOT_FOUND)
     # get path below drive letter
     if path_without_drive and path_without_drive[0] == u'\\':
         # absolute path specified
         elements = path_without_drive.split(u'\\')
     else:
         elements = self.cwd.split(os.sep) + path_without_drive.split(u'\\')
     # strip whitespace
     elements = map(unicode.strip, elements)
     # whatever's after the last \\ is the name of the subject file or dir
     # if the path ends in \\, there's no name
     name = u'' if (join_name or not elements) else elements.pop()
     # parse internal .. and . (like normpath but with \\)
     # drop leading . and .. (this is what GW-BASIC does at drive root)
     i = 0
     while i < len(elements):
         if elements[i] == u'.':
             del elements[i]
         elif elements[i] == u'..':
             del elements[i]
             if i > 0:
                 del elements[i - 1]
                 i -= 1
         else:
             i += 1
     # prepend drive root path to allow filename matching
     path = self.path
     baselen = len(path) + (path[-1] != os.sep)
     # find the native matches for each step in the path
     for e in elements:
         # skip double slashes
         if e:
             # find a matching directory for every step in the path;
             # append found name to path
             path = os.path.join(
                 path,
                 match_filename(e, b'', path, name_err=path_err,
                                isdir=True))
     # return drive root path, relative path, file name
     return path[:baselen], path[baselen:], name
Exemple #20
0
def pass_double(num):
    """ Check if variable is numeric, convert to Double. """
    if not num:
        raise error.RunError(error.STX)
    typechar = num[0]
    if typechar == '#':
        return num
    elif typechar == '%':
        return fp.pack(fp.Double.from_int(integer_to_int_signed(num)))
    elif typechar == '!':
        return ('#', bytearray(4) + num[1])
    elif typechar == '$':
        raise error.RunError(error.TYPE_MISMATCH)
Exemple #21
0
def open_file(number,
              description,
              filetype,
              mode='I',
              access='R',
              lock='',
              reclen=128,
              seg=0,
              offset=0,
              length=0):
    """ Open a file on a device specified by description. """
    if (not description) or (number < 0) or (number > max_files):
        # bad file number; also for name='', for some reason
        raise error.RunError(error.BAD_FILE_NUMBER)
    if number in state.io_state.files:
        raise error.RunError(error.FILE_ALREADY_OPEN)
    name, mode = str(description), mode.upper()
    inst = None
    split_colon = name.split(':')
    if len(split_colon) > 1:  # : found
        dev_name = split_colon[0].upper() + ':'
        dev_param = ''.join(split_colon[1:])
        try:
            device = state.io_state.devices[dev_name]
        except KeyError:
            # not an allowable device or drive name
            # bad file number, for some reason
            raise error.RunError(error.BAD_FILE_NUMBER)
    else:
        device = state.io_state.current_device
        # MS-DOS device aliases - these can't be names of disk files
        if device != state.io_state.devices['CAS1:'] and name in device_files:
            if name == 'AUX':
                device, dev_param = state.io_state.devices['COM1:'], ''
            elif name == 'CON' and mode == 'I':
                device, dev_param = state.io_state.devices['KYBD:'], ''
            elif name == 'CON' and mode == 'O':
                device, dev_param = state.io_state.devices['SCRN:'], ''
            elif name == 'PRN':
                device, dev_param = state.io_state.devices['LPT1:'], ''
            elif name == 'NUL':
                device, dev_param = NullDevice(), ''
        else:
            # open file on default device
            dev_param = name
    # open the file on the device
    new_file = device.open(number, dev_param, filetype, mode, access, lock,
                           reclen, seg, offset, length)
    if number:
        state.io_state.files[number] = new_file
    return new_file
Exemple #22
0
def pass_single(num):
    """ Check if variable is numeric, convert to Single. """
    if not num:
        raise error.RunError(error.STX)
    typechar = num[0]
    if typechar == '!':
        return num
    elif typechar == '%':
        return fp.pack(fp.Single.from_int(integer_to_int_signed(num)))
    elif typechar == '#':
        # *round* to single
        return fp.pack(fp.unpack(num).round_to_single())
    elif typechar == '$':
        raise error.RunError(error.TYPE_MISMATCH)
Exemple #23
0
def get_diskdevice_and_path(path):
    """ Return the disk device and remaining path for given BASIC path. """
    splits = str(path).upper().split(':', 1)
    if len(splits) == 0:
        return state.io_state.current_device, ''
    elif len(splits) == 1:
        return state.io_state.current_device, splits[0]
    else:
        # must be a disk device
        if len(splits[0]) > 1:
            raise error.RunError(error.DEVICE_UNAVAILABLE)
        try:
            return state.io_state.devices[splits[0] + ':'], splits[1]
        except KeyError:
            raise error.RunError(error.DEVICE_UNAVAILABLE)
Exemple #24
0
 def attach_var(self, name, indices, offset, length):
     """ Attach a FIELD variable. """
     if name[-1] != '$':
         # type mismatch
         raise error.RunError(error.TYPE_MISMATCH)
     if offset + length > len(self.buffer):
         # FIELD overflow
         raise error.RunError(error.FIELD_OVERFLOW)
     # create a string pointer
     str_addr = self.address + offset
     str_sequence = chr(length) + vartypes.integer_to_bytes(
         vartypes.int_to_integer_unsigned(str_addr))
     # assign the string ptr to the variable name
     # desired side effect: if we re-assign this string variable through LET, it's no longer connected to the FIELD.
     var.set_variable(name, indices, vartypes.bytes_to_string(str_sequence))
Exemple #25
0
def get_value_for_varptrstr(varptrstr):
    """ Get a value given a VARPTR$ representation. """
    if len(varptrstr) < 3:
        raise error.RunError(error.IFC)
    varptrstr = bytearray(varptrstr)
    varptr = vartypes.uint_to_value(bytearray(varptrstr[1:3]))
    found_name = ''
    for name in state.basic_state.var_memory:
        _, var_ptr = state.basic_state.var_memory[name]
        if var_ptr == varptr:
            found_name = name
            break
    if found_name == '':
        raise error.RunError(error.IFC)
    return var.get_var(found_name)
Exemple #26
0
def get_var_name(ins, allow_empty=False):
    """ Get variable name from token stream. """
    name = ''
    d = skip_white_read(ins).upper()
    if not d:
        pass
    elif d not in string.ascii_uppercase:
        # variable name must start with a letter
        ins.seek(-len(d), 1)
    else:
        while d and d in name_chars:
            name += d
            d = ins.read(1).upper()
        if d in '$%!#':
            name += d
        else:
            ins.seek(-len(d), 1)
    if not name and not allow_empty:
        raise error.RunError(error.STX)
    # append type specifier
    name = vartypes.complete_name(name)
    # only the first 40 chars are relevant in GW-BASIC, rest is discarded
    if len(name) > 41:
        name = name[:40] + name[-1]
    return name
Exemple #27
0
 def open(self, rs=False, cs=1000, ds=1000, cd=0):
     """ Open the serial connection. """
     self._serial.open()
     # handshake
     # by default, RTS is up, DTR down
     # RTS can be suppressed, DTR only accessible through machine ports
     # https://lbpe.wikispaces.com/AccessingSerialPort
     if not rs:
         self._serial.setRTS(True)
     now = datetime.datetime.now()
     timeout_cts = now + datetime.timedelta(microseconds=cs)
     timeout_dsr = now + datetime.timedelta(microseconds=ds)
     timeout_cd = now + datetime.timedelta(microseconds=cd)
     have_cts, have_dsr, have_cd = False, False, False
     while ((now < timeout_cts and not have_cts) and
             (now < timeout_dsr and not have_dsr) and
             (now < timeout_cd and not have_cd)):
         now = datetime.datetime.now()
         have_cts = have_cts and self._serial.getCTS()
         have_dsr = have_dsr and self._serial.getDSR()
         have_cts = have_cd and self._serial.getCD()
         # update screen, give CPU some time off
         backend.idle()
     # only check for status if timeouts are set > 0
     # http://www.electro-tech-online.com/threads/qbasic-serial-port-control.19286/
     # https://measurementsensors.honeywell.com/ProductDocuments/Instruments/008-0385-00.pdf
     if ((cs > 0 and not have_cts) or
             (ds > 0 and not have_dsr) or
             (cd > 0 and not have_cd)):
         raise error.RunError(error.DEVICE_TIMEOUT)
     self.is_open = True
Exemple #28
0
 def check_read(self, allow_overflow=False):
     """ Fill buffer at most up to buffer size; non blocking. """
     try:
         self.in_buffer += self.fhandle.read(serial_in_size - len(self.in_buffer))
     except (EnvironmentError, ValueError):
         raise error.RunError(error.DEVICE_IO_ERROR)
     # if more to read, signal an overflow
     if len(self.in_buffer) >= serial_in_size and self.fhandle.read(1):
         self.overflow = True
         # drop waiting chars that don't fit in buffer
         while self.fhandle.read(1):
             pass
     if not allow_overflow and self.overflow:
         # only raise this the first time the overflow is encountered
         self.overflow = False
         raise error.RunError(error.COMMUNICATION_BUFFER_OVERFLOW)
Exemple #29
0
def edit(from_line, bytepos=None):
    """ Output program line to console and position cursor. """
    if state.basic_state.protected:
        console.write(str(from_line) + '\r')
        raise error.RunError(error.IFC)
    # list line
    state.basic_state.bytecode.seek(state.basic_state.line_numbers[from_line] +
                                    1)
    _, output, textpos = tokenise.detokenise_line(state.basic_state.bytecode,
                                                  bytepos)
    # no newline to avoid scrolling on line 24
    console.list_line(str(output), newline=False)
    # find row, column position for textpos
    newlines, c = 0, 0
    pos_row, pos_col = 0, 0
    for i, byte in enumerate(output):
        c += 1
        if chr(byte) == '\n' or c > state.console_state.screen.mode.width:
            newlines += 1
            c = 0
        if i == textpos:
            pos_row, pos_col = newlines, c
    if textpos > i:
        pos_row, pos_col = newlines, c + 1
    if bytepos:
        console.set_pos(state.console_state.row - newlines + pos_row, pos_col)
    else:
        console.set_pos(state.console_state.row - newlines, 1)
Exemple #30
0
def pass_integer(inp, maxint=0x7fff, err=error.TYPE_MISMATCH):
    """ Check if variable is numeric, convert to Int. """
    if not inp:
        raise error.RunError(error.STX)
    typechar = inp[0]
    if typechar == '%':
        return inp
    elif typechar in ('!', '#'):
        val = fp.unpack(inp).round_to_int()
        if val > maxint or val < -0x8000:
            # overflow
            raise error.RunError(error.OVERFLOW)
        return int_to_integer_unsigned(val)
    else:
        # type mismatch
        raise error.RunError(err)