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
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)
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)
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)))
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
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)
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
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
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
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)
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
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)
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
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
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
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())
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)
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)
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
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)
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
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)
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)
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))
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)
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
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
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)
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)
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)