def get_data_memory_array(address): """ Retrieve data from data memory: array space """ name_addr = -1 arr_addr = -1 the_arr = None for name in state.basic_state.array_memory: name_try, arr_try = state.basic_state.array_memory[name] if name_try <= address and name_try > name_addr: name_addr, arr_addr = name_try, arr_try the_arr = name if the_arr is None: return -1 if address >= state.basic_state.var_current + arr_addr: offset = address - arr_addr - state.basic_state.var_current if offset >= var.array_size_bytes(the_arr): return -1 _, byte_array, _ = state.basic_state.arrays[the_arr] return byte_array[offset] else: offset = address - name_addr - state.basic_state.var_current if offset < max(3, len(the_arr)) + 1: return get_name_in_memory(the_arr, offset) else: offset -= max(3, len(the_arr)) + 1 dimensions, _, _ = state.basic_state.arrays[the_arr] data_rep = vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned( var.array_size_bytes(the_arr) + 1 + 2 * len(dimensions)) + chr(len(dimensions))) for d in dimensions: data_rep += vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned( d + 1 - state.basic_state.array_base)) return data_rep[offset]
def get_data_memory_array(address): """ Retrieve data from data memory: array space """ name_addr = -1 arr_addr = -1 the_arr = None for name in state.basic_state.array_memory: name_try, arr_try = state.basic_state.array_memory[name] if name_try <= address and name_try > name_addr: name_addr, arr_addr = name_try, arr_try the_arr = name if the_arr is None: return -1 if address >= state.basic_state.var_current + arr_addr: offset = address - arr_addr - state.basic_state.var_current if offset >= var.array_size_bytes(the_arr): return -1 _, byte_array, _ = state.basic_state.arrays[the_arr] return byte_array[offset] else: offset = address - name_addr - state.basic_state.var_current if offset < max(3, len(the_arr))+1: return get_name_in_memory(the_arr, offset) else: offset -= max(3, len(the_arr))+1 dimensions, _, _ = state.basic_state.arrays[the_arr] data_rep = vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned( var.array_size_bytes(the_arr) + 1 + 2*len(dimensions)) + chr(len(dimensions))) for d in dimensions: data_rep += vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned( d + 1 - state.basic_state.array_base)) return data_rep[offset]
def __init__(self, fhandle, filetype, number, name, mode, seg, offset, length): """ Initialise program file object and write header. """ devices.RawFile.__init__(self, fhandle, filetype, mode) self.number = number # don't lock binary files self.lock = b'' self.access = b'RW' self.seg, self.offset, self.length = 0, 0, 0 if self.mode == b'O': self.write(devices.type_to_magic[filetype]) if self.filetype == b'M': self.write( vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(seg)) + vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(offset)) + vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(length))) self.seg, self.offset, self.length = seg, offset, length else: # drop magic byte self.read_raw(1) if self.filetype == b'M': self.seg = vartypes.integer_to_int_unsigned( vartypes.bytes_to_integer(self.read(2))) self.offset = vartypes.integer_to_int_unsigned( vartypes.bytes_to_integer(self.read(2))) # size gets ignored: even the \x1a at the end is read self.length = vartypes.integer_to_int_unsigned( vartypes.bytes_to_integer(self.read(2)))
def bsave(g, offset, length): """ Save a block of memory into a file. """ addr = state.basic_state.segment * 0x10 + offset g.write(str(get_memory_block(addr, length))) # Tandys repeat the header at the end of the file if tandy_syntax: g.write('\xfd' + str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(state.basic_state.segment)) + vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(offset)) + vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(length))))
def bsave(g, offset, length): """ Save a block of memory into a file. """ addr = state.basic_state.segment * 0x10 + offset g.write(str(get_memory_block(addr, length))) # Tandys repeat the header at the end of the file if tandy_syntax: g.write('\xfd' + str( vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(state.basic_state.segment)) + vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned( offset)) + vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(length))))
def tokenise_uint(ins): """ Convert an unsigned int (line number) to tokenised form. """ word = bytearray() while True: c = ins.read(1) if c and c in string.digits + ascii_whitespace: word += c else: ins.seek(-len(c), 1) break # don't claim trailing w/s while len(word)>0 and chr(word[-1]) in ascii_whitespace: del word[-1] ins.seek(-1, 1) # remove all whitespace trimword = bytearray() for c in word: if chr(c) not in ascii_whitespace: trimword += chr(c) word = trimword # line number (jump) if len(word) > 0: if int(word) >= 65530: # note: anything >= 65530 is illegal in GW-BASIC # in loading an ASCII file, GWBASIC would interpret these as # '6553 1' etcetera, generating a syntax error on load. # keep 6553 as line number and push back the last number: ins.seek(4-len(word), 1) word = word[:4] return str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(int(word)))) else: return ''
def rebuild_line_dict(): """ Preparse to build line number dictionary. """ state.basic_state.line_numbers, offsets = {}, [] state.basic_state.bytecode.seek(0) scanline, scanpos, last = 0, 0, 0 while True: state.basic_state.bytecode.read(1) # pass \x00 scanline = util.parse_line_number(state.basic_state.bytecode) if scanline == -1: scanline = 65536 # if parse_line_number returns -1, it leaves the stream pointer here: 00 _00_ 00 1A break state.basic_state.line_numbers[scanline] = scanpos last = scanpos util.skip_to(state.basic_state.bytecode, tk.end_line) scanpos = state.basic_state.bytecode.tell() offsets.append(scanpos) state.basic_state.line_numbers[65536] = scanpos # rebuild offsets state.basic_state.bytecode.seek(0) last = 0 for pos in offsets: state.basic_state.bytecode.read(1) state.basic_state.bytecode.write( str( vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(program_memory_start + pos)))) state.basic_state.bytecode.read(pos - last - 3) last = pos # ensure program is properly sealed - last offset must be 00 00. keep, but ignore, anything after. state.basic_state.bytecode.write('\0\0\0')
def store_line(linebuf): """ Store the given line buffer. """ if state.basic_state.protected: raise error.RunError(error.IFC) # get the new line number linebuf.seek(1) scanline = util.parse_line_number(linebuf) # check if linebuf is an empty line after the line number empty = (util.skip_white_read(linebuf) in tk.end_line) pos, afterpos, deleteable, beyond = find_pos_line_dict(scanline, scanline) if empty and not deleteable: raise error.RunError(error.UNDEFINED_LINE_NUMBER) # read the remainder of the program into a buffer to be pasted back after the write state.basic_state.bytecode.seek(afterpos) rest = state.basic_state.bytecode.read() # insert state.basic_state.bytecode.seek(pos) # write the line buffer to the program buffer length = 0 if not empty: # set offsets linebuf.seek(3) # pass \x00\xC0\xDE length = len(linebuf.getvalue()) state.basic_state.bytecode.write( '\0' + str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(program_memory_start + pos + length))) + linebuf.read()) # write back the remainder of the program truncate_program(rest) # update all next offsets by shifting them by the length of the added line update_line_dict(pos, afterpos, length, deleteable, beyond) if not empty: state.basic_state.line_numbers[scanline] = pos # clear all program stacks flow.init_program() state.basic_state.last_stored = scanline
def rebuild_line_dict(): """ Preparse to build line number dictionary. """ state.basic_state.line_numbers, offsets = {}, [] state.basic_state.bytecode.seek(0) scanline, scanpos, last = 0, 0, 0 while True: state.basic_state.bytecode.read(1) # pass \x00 scanline = util.parse_line_number(state.basic_state.bytecode) if scanline == -1: scanline = 65536 # if parse_line_number returns -1, it leaves the stream pointer here: 00 _00_ 00 1A break state.basic_state.line_numbers[scanline] = scanpos last = scanpos util.skip_to(state.basic_state.bytecode, tk.end_line) scanpos = state.basic_state.bytecode.tell() offsets.append(scanpos) state.basic_state.line_numbers[65536] = scanpos # rebuild offsets state.basic_state.bytecode.seek(0) last = 0 for pos in offsets: state.basic_state.bytecode.read(1) state.basic_state.bytecode.write(str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(program_memory_start + pos)))) state.basic_state.bytecode.read(pos - last - 3) last = pos # ensure program is properly sealed - last offset must be 00 00. keep, but ignore, anything after. state.basic_state.bytecode.write('\0\0\0')
def tokenise_uint(ins): """ Convert an unsigned int (line number) to tokenised form. """ word = bytearray() while True: c = ins.read(1) if c and c in string.digits + ascii_whitespace: word += c else: ins.seek(-len(c), 1) break # don't claim trailing w/s while len(word) > 0 and chr(word[-1]) in ascii_whitespace: del word[-1] ins.seek(-1, 1) # remove all whitespace trimword = bytearray() for c in word: if chr(c) not in ascii_whitespace: trimword += chr(c) word = trimword # line number (jump) if len(word) > 0: if int(word) >= 65530: # note: anything >= 65530 is illegal in GW-BASIC # in loading an ASCII file, GWBASIC would interpret these as # '6553 1' etcetera, generating a syntax error on load. # keep 6553 as line number and push back the last number: ins.seek(4 - len(word), 1) word = word[:4] return str( vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(int(word)))) else: return ''
def delete_last(self): """ Delete the string provided if it is at the top of string space. """ last_address = self.current + 1 last_key = str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(last_address))) try: length = len(self.strings[last_key]) self.current += length del self.strings[last_key] except KeyError: # happens if we're called before an out-of-memory exception is handled # and the string wasn't allocated pass
def number_inc_gt(typechar, loopvar, stop, step, sgn): """ Increase number and check if it exceeds a limit. """ if sgn == 0: return False if typechar in ('#', '!'): fp_left = fp.from_bytes(loopvar).iadd(step) loopvar[:] = fp_left.to_bytes() return fp_left.gt(stop) if sgn > 0 else stop.gt(fp_left) else: int_left = vartypes.integer_to_int_signed(vartypes.bytes_to_integer(loopvar)) + step loopvar[:] = vartypes.integer_to_bytes(vartypes.int_to_integer_signed(int_left)) return int_left > stop if sgn > 0 else stop > int_left
def delete_last(self): """ Delete the string provided if it is at the top of string space. """ last_address = self.current + 1 last_key = str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(last_address))) try: length = len(self.strings[last_key]) self.current += length del self.strings[last_key] except KeyError: # happens if we're called before an out-of-memory exception is handled # and the string wasn't allocated pass
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 number_inc_gt(typechar, loopvar, stop, step, sgn): """ Increase number and check if it exceeds a limit. """ if sgn == 0: return False if typechar in ('#', '!'): fp_left = fp.from_bytes(loopvar).iadd(step) loopvar[:] = fp_left.to_bytes() return fp_left.gt(stop) if sgn > 0 else stop.gt(fp_left) else: int_left = vartypes.integer_to_int_signed( vartypes.bytes_to_integer(loopvar)) + step loopvar[:] = vartypes.integer_to_bytes( vartypes.int_to_integer_signed(int_left)) return int_left > stop if sgn > 0 else stop > int_left
def tokenise_hex(ins, outs): """ Convert hex expression in Python string to number token. """ # pass the H in &H ins.read(1) word = '' while True: c = util.peek(ins) # hex literals must not be interrupted by whitespace if not c or c not in string.hexdigits: break else: word += ins.read(1) val = int(word, 16) if word else 0 outs.write(tk.T_HEX + str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(val))))
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 __init__(self, fhandle, filetype, number, name, mode, seg, offset, length): """ Initialise program file object and write header. """ devices.RawFile.__init__(self, fhandle, filetype, mode) self.number = number # don't lock binary files self.lock = b'' self.access = b'RW' self.seg, self.offset, self.length = 0, 0, 0 if self.mode == b'O': self.write(devices.type_to_magic[filetype]) if self.filetype == b'M': self.write(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(seg)) + vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(offset)) + vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(length))) self.seg, self.offset, self.length = seg, offset, length else: # drop magic byte self.read_raw(1) if self.filetype == b'M': self.seg = vartypes.integer_to_int_unsigned(vartypes.bytes_to_integer(self.read(2))) self.offset = vartypes.integer_to_int_unsigned(vartypes.bytes_to_integer(self.read(2))) # size gets ignored: even the \x1a at the end is read self.length = vartypes.integer_to_int_unsigned(vartypes.bytes_to_integer(self.read(2)))
def tokenise_hex(ins, outs): """ Convert hex expression in Python string to number token. """ # pass the H in &H ins.read(1) word = '' while True: c = util.peek(ins) # hex literals must not be interrupted by whitespace if not c or c not in string.hexdigits: break else: word += ins.read(1) val = int(word, 16) if word else 0 outs.write( tk.T_HEX + str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(val))))
def tokenise_oct(ins, outs): """ Convert octal expression in Python string to number token. """ # O is optional, could also be &777 instead of &O777 if util.peek(ins).upper() == 'O': ins.read(1) word = '' while True: c = util.peek(ins) # oct literals may be interrupted by whitespace if c and c in number_whitespace: ins.read(1) elif not c or c not in string.octdigits: break else: word += ins.read(1) val = int(word, 8) if word else 0 outs.write(tk.T_OCT + str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(val))))
def value_varptr(ins): """ VARPTR, VARPTR$: get memory address for variable or FCB. """ dollar = util.skip_white_read_if(ins, ('$',)) util.require_read(ins, ('(',)) if (not dollar) and util.skip_white(ins) == '#': filenum = parse_file_number_opthash(ins) var_ptr = machine.varptr_file(filenum) else: name, indices = parse_variable(ins) var_ptr = machine.varptr(name, indices) util.require_read(ins, (')',)) if var_ptr < 0: raise error.RunError(error.IFC) var_ptr = vartypes.int_to_integer_unsigned(var_ptr) if dollar: return state.basic_state.strings.store(chr(vartypes.byte_size[name[-1]]) + vartypes.integer_to_bytes(var_ptr)) else: return var_ptr
def tokenise_oct(ins, outs): """ Convert octal expression in Python string to number token. """ # O is optional, could also be &777 instead of &O777 if util.peek(ins).upper() == 'O': ins.read(1) word = '' while True: c = util.peek(ins) # oct literals may be interrupted by whitespace if c and c in number_whitespace: ins.read(1) elif not c or c not in string.octdigits: break else: word += ins.read(1) val = int(word, 8) if word else 0 outs.write( tk.T_OCT + str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(val))))
def store(self, in_str, address=None): """ Store a new string and return the string pointer. """ size = len(in_str) # don't store overlong strings if size > 255: raise error.RunError(error.STRING_TOO_LONG) if address is None: # reserve string space; collect garbage if necessary check_free_memory(size, error.OUT_OF_STRING_SPACE) # find new string address self.current -= size address = self.current + 1 key = str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(address))) # don't store empty strings if size > 0: if key in self.strings: logging.debug('String key %s at %d already defined.' % (repr(key), address)) # copy and convert to bytearray self.strings[key] = bytearray(in_str) return vartypes.bytes_to_string(chr(size) + key)
def value_varptr(ins): """ VARPTR, VARPTR$: get memory address for variable or FCB. """ dollar = util.skip_white_read_if(ins, ('$', )) util.require_read(ins, ('(', )) if (not dollar) and util.skip_white(ins) == '#': filenum = parse_file_number_opthash(ins) var_ptr = machine.varptr_file(filenum) else: name, indices = parse_variable(ins) var_ptr = machine.varptr(name, indices) util.require_read(ins, (')', )) if var_ptr < 0: raise error.RunError(error.IFC) var_ptr = vartypes.int_to_integer_unsigned(var_ptr) if dollar: return state.basic_state.strings.store( chr(vartypes.byte_size[name[-1]]) + vartypes.integer_to_bytes(var_ptr)) else: return var_ptr
def store(self, in_str, address=None): """ Store a new string and return the string pointer. """ size = len(in_str) # don't store overlong strings if size > 255: raise error.RunError(error.STRING_TOO_LONG) if address is None: # reserve string space; collect garbage if necessary check_free_memory(size, error.OUT_OF_STRING_SPACE) # find new string address self.current -= size address = self.current + 1 key = str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(address))) # don't store empty strings if size > 0: if key in self.strings: logging.debug('String key %s at %d already defined.' % (repr(key), address)) # copy and convert to bytearray self.strings[key] = bytearray(in_str) return vartypes.bytes_to_string(chr(size) + key)
def update_line_dict(pos, afterpos, length, deleteable, beyond): """ Update line number dictionary after deleting lines. """ # subtract length of line we replaced length -= afterpos - pos addr = program_memory_start + afterpos state.basic_state.bytecode.seek(afterpos + length + 1) # pass \x00 while True: next_addr = state.basic_state.bytecode.read(2) if len(next_addr) < 2 or next_addr == '\0\0': break next_addr = vartypes.integer_to_int_unsigned(vartypes.bytes_to_integer(next_addr)) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write(str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(next_addr + length)))) state.basic_state.bytecode.read(next_addr - addr - 2) addr = next_addr # update line number dict for key in deleteable: del state.basic_state.line_numbers[key] for key in beyond: state.basic_state.line_numbers[key] += length
def update_line_dict(pos, afterpos, length, deleteable, beyond): """ Update line number dictionary after deleting lines. """ # subtract length of line we replaced length -= afterpos - pos addr = program_memory_start + afterpos state.basic_state.bytecode.seek(afterpos + length + 1) # pass \x00 while True: next_addr = state.basic_state.bytecode.read(2) if len(next_addr) < 2 or next_addr == '\0\0': break next_addr = vartypes.integer_to_int_unsigned( vartypes.bytes_to_integer(next_addr)) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write( str( vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(next_addr + length)))) state.basic_state.bytecode.read(next_addr - addr - 2) addr = next_addr # update line number dict for key in deleteable: del state.basic_state.line_numbers[key] for key in beyond: state.basic_state.line_numbers[key] += length
def store_line(linebuf): """ Store the given line buffer. """ if state.basic_state.protected: raise error.RunError(error.IFC) # get the new line number linebuf.seek(1) scanline = util.parse_line_number(linebuf) # check if linebuf is an empty line after the line number empty = (util.skip_white_read(linebuf) in tk.end_line) pos, afterpos, deleteable, beyond = find_pos_line_dict(scanline, scanline) if empty and not deleteable: raise error.RunError(error.UNDEFINED_LINE_NUMBER) # read the remainder of the program into a buffer to be pasted back after the write state.basic_state.bytecode.seek(afterpos) rest = state.basic_state.bytecode.read() # insert state.basic_state.bytecode.seek(pos) # write the line buffer to the program buffer length = 0 if not empty: # set offsets linebuf.seek(3) # pass \x00\xC0\xDE length = len(linebuf.getvalue()) state.basic_state.bytecode.write('\0' + str( vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(program_memory_start + pos + length))) + linebuf.read()) # write back the remainder of the program truncate_program(rest) # update all next offsets by shifting them by the length of the added line update_line_dict(pos, afterpos, length, deleteable, beyond) if not empty: state.basic_state.line_numbers[scanline] = pos # clear all program stacks flow.init_program() state.basic_state.last_stored = scanline
def value_mki(ins): """ MKI$: return the byte representation of an int. """ return state.basic_state.strings.store( vartypes.integer_to_bytes(vartypes.pass_integer(parse_bracket(ins))))
def tokenise_dec(ins, outs): """ Convert decimal expression in Python string to number token. """ have_exp = False have_point = False word = '' kill = False while True: c = ins.read(1).upper() if not c: break elif c in '\x1c\x1d\x1f': # ASCII separator chars invariably lead to zero result kill = True elif c == '.' and not have_point and not have_exp: have_point = True word += c elif c in 'ED' and not have_exp: # there's a special exception for number followed by EL or EQ # presumably meant to protect ELSE and maybe EQV ? if c == 'E' and util.peek(ins).upper() in ('L', 'Q'): ins.seek(-1, 1) break else: have_exp = True word += c elif c in '-+' and (not word or word[-1] in 'ED'): # must be first token or in exponent word += c elif c in string.digits: word += c elif c in number_whitespace: # we'll remove this later but need to keep it for now # so we can reposition the stream on removing trailing whitespace word += c elif c in '!#' and not have_exp: word += c break elif c == '%': # swallow a %, but break parsing break else: ins.seek(-1, 1) break # ascii separators encountered: zero output if kill: word = '0' # don't claim trailing whitespace while len(word)>0 and (word[-1] in number_whitespace): word = word[:-1] ins.seek(-1,1) # even if c=='' # remove all internal whitespace trimword = '' for c in word: if c not in number_whitespace: trimword += c word = trimword # write out the numbers if len(word) == 1 and word in string.digits: # digit outs.write(chr(0x11+str_to_int(word))) elif (not (have_exp or have_point or word[-1] in '!#') and str_to_int(word) <= 0x7fff and str_to_int(word) >= -0x8000): if str_to_int(word) <= 0xff and str_to_int(word) >= 0: # one-byte constant outs.write(tk.T_BYTE + chr(str_to_int(word))) else: # two-byte constant outs.write(tk.T_INT + str(vartypes.integer_to_bytes(vartypes.int_to_integer_signed(str_to_int(word))))) else: mbf = str(str_to_float(word).to_bytes()) if len(mbf) == 4: outs.write(tk.T_SINGLE + mbf) else: outs.write(tk.T_DOUBLE + mbf)
def value_mki(ins): """ MKI$: return the byte representation of an int. """ return state.basic_state.strings.store(vartypes.integer_to_bytes(vartypes.pass_integer(parse_bracket(ins))))
def renum(new_line, start_line, step): """ Renumber stored program. """ new_line = 10 if new_line is None else new_line start_line = 0 if start_line is None else start_line step = 10 if step is None else step # get a sorted list of line numbers keys = sorted([ k for k in state.basic_state.line_numbers.keys() if k >= start_line]) # assign the new numbers old_to_new = {} for old_line in keys: if old_line < 65535 and new_line > 65529: raise error.RunError(error.IFC) if old_line == 65536: break old_to_new[old_line] = new_line state.basic_state.last_stored = new_line new_line += step # write the new numbers for old_line in old_to_new: state.basic_state.bytecode.seek(state.basic_state.line_numbers[old_line]) # skip the \x00\xC0\xDE & overwrite line number state.basic_state.bytecode.read(3) state.basic_state.bytecode.write(str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(old_to_new[old_line])))) # write the indirect line numbers ins = state.basic_state.bytecode ins.seek(0) while util.skip_to_read(ins, (tk.T_UINT,)) == tk.T_UINT: # get the old g number jumpnum = vartypes.integer_to_int_unsigned(vartypes.bytes_to_integer(ins.read(2))) # handle exception for ERROR GOTO if jumpnum == 0: pos = ins.tell() # skip line number token ins.seek(-3, 1) if util.backskip_white(ins) == tk.GOTO and util.backskip_white(ins) == tk.ERROR: ins.seek(pos) continue ins.seek(pos) try: newjump = old_to_new[jumpnum] except KeyError: # not redefined, exists in program? if jumpnum not in state.basic_state.line_numbers: linum = get_line_number(ins.tell()-1) console.write_line('Undefined line ' + str(jumpnum) + ' in ' + str(linum)) newjump = jumpnum ins.seek(-2, 1) ins.write(str(vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(newjump)))) # rebuild the line number dictionary new_lines = {} for old_line in old_to_new: new_lines[old_to_new[old_line]] = state.basic_state.line_numbers[old_line] del state.basic_state.line_numbers[old_line] state.basic_state.line_numbers.update(new_lines) # stop running if we were flow.set_pointer(False) # reset loop stacks state.basic_state.gosub_return = [] state.basic_state.for_next_stack = [] state.basic_state.while_wend_stack = [] # renumber error handler if state.basic_state.on_error: state.basic_state.on_error = old_to_new[state.basic_state.on_error] # renumber event traps for handler in state.basic_state.events.all: if handler.gosub: handler.set_jump(old_to_new[handler.gosub])
def renum(new_line, start_line, step): """ Renumber stored program. """ new_line = 10 if new_line is None else new_line start_line = 0 if start_line is None else start_line step = 10 if step is None else step # get a sorted list of line numbers keys = sorted( [k for k in state.basic_state.line_numbers.keys() if k >= start_line]) # assign the new numbers old_to_new = {} for old_line in keys: if old_line < 65535 and new_line > 65529: raise error.RunError(error.IFC) if old_line == 65536: break old_to_new[old_line] = new_line state.basic_state.last_stored = new_line new_line += step # write the new numbers for old_line in old_to_new: state.basic_state.bytecode.seek( state.basic_state.line_numbers[old_line]) # skip the \x00\xC0\xDE & overwrite line number state.basic_state.bytecode.read(3) state.basic_state.bytecode.write( str( vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(old_to_new[old_line])))) # write the indirect line numbers ins = state.basic_state.bytecode ins.seek(0) while util.skip_to_read(ins, (tk.T_UINT, )) == tk.T_UINT: # get the old g number jumpnum = vartypes.integer_to_int_unsigned( vartypes.bytes_to_integer(ins.read(2))) # handle exception for ERROR GOTO if jumpnum == 0: pos = ins.tell() # skip line number token ins.seek(-3, 1) if util.backskip_white(ins) == tk.GOTO and util.backskip_white( ins) == tk.ERROR: ins.seek(pos) continue ins.seek(pos) try: newjump = old_to_new[jumpnum] except KeyError: # not redefined, exists in program? if jumpnum not in state.basic_state.line_numbers: linum = get_line_number(ins.tell() - 1) console.write_line('Undefined line ' + str(jumpnum) + ' in ' + str(linum)) newjump = jumpnum ins.seek(-2, 1) ins.write( str( vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(newjump)))) # rebuild the line number dictionary new_lines = {} for old_line in old_to_new: new_lines[ old_to_new[old_line]] = state.basic_state.line_numbers[old_line] del state.basic_state.line_numbers[old_line] state.basic_state.line_numbers.update(new_lines) # stop running if we were flow.set_pointer(False) # reset loop stacks state.basic_state.gosub_return = [] state.basic_state.for_next_stack = [] state.basic_state.while_wend_stack = [] # renumber error handler if state.basic_state.on_error: state.basic_state.on_error = old_to_new[state.basic_state.on_error] # renumber event traps for handler in state.basic_state.events.all: if handler.gosub: handler.set_jump(old_to_new[handler.gosub])
def tokenise_dec(ins, outs): """ Convert decimal expression in Python string to number token. """ have_exp = False have_point = False word = '' kill = False while True: c = ins.read(1).upper() if not c: break elif c in '\x1c\x1d\x1f': # ASCII separator chars invariably lead to zero result kill = True elif c == '.' and not have_point and not have_exp: have_point = True word += c elif c in 'ED' and not have_exp: # there's a special exception for number followed by EL or EQ # presumably meant to protect ELSE and maybe EQV ? if c == 'E' and util.peek(ins).upper() in ('L', 'Q'): ins.seek(-1, 1) break else: have_exp = True word += c elif c in '-+' and (not word or word[-1] in 'ED'): # must be first token or in exponent word += c elif c in string.digits: word += c elif c in number_whitespace: # we'll remove this later but need to keep it for now # so we can reposition the stream on removing trailing whitespace word += c elif c in '!#' and not have_exp: word += c break elif c == '%': # swallow a %, but break parsing break else: ins.seek(-1, 1) break # ascii separators encountered: zero output if kill: word = '0' # don't claim trailing whitespace while len(word) > 0 and (word[-1] in number_whitespace): word = word[:-1] ins.seek(-1, 1) # even if c=='' # remove all internal whitespace trimword = '' for c in word: if c not in number_whitespace: trimword += c word = trimword # write out the numbers if len(word) == 1 and word in string.digits: # digit outs.write(chr(0x11 + str_to_int(word))) elif (not (have_exp or have_point or word[-1] in '!#') and str_to_int(word) <= 0x7fff and str_to_int(word) >= -0x8000): if str_to_int(word) <= 0xff and str_to_int(word) >= 0: # one-byte constant outs.write(tk.T_BYTE + chr(str_to_int(word))) else: # two-byte constant outs.write(tk.T_INT + str( vartypes.integer_to_bytes( vartypes.int_to_integer_signed(str_to_int(word))))) else: mbf = str(str_to_float(word).to_bytes()) if len(mbf) == 4: outs.write(tk.T_SINGLE + mbf) else: outs.write(tk.T_DOUBLE + mbf)
def sprite_size_to_record(self, dx, dy): """ Write 4-byte record of sprite size. """ return ( vartypes.integer_to_bytes( vartypes.int_to_integer_unsigned(dx * self.bitsperpixel)) + vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(dy)))
def sprite_size_to_record_ega(self, dx, dy): """ Write 4-byte record of sprite size in EGA modes. """ return (vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(dx)) + vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(dy)))
def sprite_size_to_record(self, dx, dy): """ Write 4-byte record of sprite size. """ return (vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(dx*self.bitsperpixel)) + vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(dy)))
def sprite_size_to_record_ega(self, dx, dy): """ Write 4-byte record of sprite size in EGA modes. """ return (vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(dx)) + vartypes.integer_to_bytes(vartypes.int_to_integer_unsigned(dy)))