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.value_to_uint(old_to_new[old_line]))) # 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) # write the indirect line numbers state.basic_state.bytecode.seek(0) while util.skip_to_read(state.basic_state.bytecode, ('\x0e',)) == '\x0e': # get the old g number jumpnum = vartypes.uint_to_value(bytearray(state.basic_state.bytecode.read(2))) try: newjump = old_to_new[jumpnum] except KeyError: # not redefined, exists in program? if jumpnum in state.basic_state.line_numbers: newjump = jumpnum else: linum = get_line_number(state.basic_state.bytecode.tell()) console.write_line('Undefined line ' + str(jumpnum) + ' in ' + str(linum)) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write(str(vartypes.value_to_uint(newjump))) # 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 string_assign_unpacked_into(sequence, offset, num, val): """ Write an unpacked value into a string buffer for given 3-byte sequence. """ # don't overwrite more of the old string than the length of the new string num = min(num, len(val)) # ensure the length of val is num, cut off any extra characters val = val[:num] length = ord(sequence[0:1]) address = vartypes.uint_to_value(sequence[-2:]) if offset + num > length: num = length - offset if num <= 0: return if address >= memory.var_start(): # string stored in string space state.basic_state.strings.retrieve(sequence)[offset:offset+num] = val else: # string stored in field buffers # find the file we're in start = address - memory.field_mem_start number = 1 + start // memory.field_mem_offset field_offset = start % memory.field_mem_offset try: state.io_state.fields[number][field_offset+offset:field_offset+offset+num] = val except KeyError, IndexError: raise KeyError('Not a field string')
def string_assign_unpacked_into(sequence, offset, num, val): """ Write an unpacked value into a string buffer for given 3-byte sequence. """ # don't overwrite more of the old string than the length of the new string num = min(num, len(val)) # ensure the length of val is num, cut off any extra characters val = val[:num] length = ord(sequence[0:1]) address = vartypes.uint_to_value(sequence[-2:]) if offset + num > length: num = length - offset if num <= 0: return if address >= memory.var_start(): # string stored in string space state.basic_state.strings.retrieve(sequence)[offset:offset + num] = val else: # string stored in field buffers # find the file we're in start = address - memory.field_mem_start number = 1 + start // memory.field_mem_offset field_offset = start % memory.field_mem_offset try: state.io_state.fields[number].buffer[field_offset + offset:field_offset + offset + num] = val except KeyError, IndexError: raise KeyError('Not a field string')
def show_program(): """ Write a marked-up hex dump of the program to the log. """ code = state.basic_state.bytecode.getvalue() offset_val, p = 0, 0 for key in sorted(state.basic_state.line_numbers.keys())[1:]: offset, linum = code[p+1:p+3], code[p+3:p+5] last_offset = offset_val offset_val = vartypes.uint_to_value(bytearray(offset)) - program.program_memory_start linum_val = vartypes.uint_to_value(bytearray(linum)) logging.debug( (code[p:p+1].encode('hex') + ' ' + offset.encode('hex') + ' (+%03d) ' + code[p+3:p+5].encode('hex') + ' [%05d] ' + code[p+5:state.basic_state.line_numbers[key]].encode('hex')), offset_val - last_offset, linum_val ) p = state.basic_state.line_numbers[key] logging.debug(code[p:p+1].encode('hex') + ' ' + code[p+1:p+3].encode('hex') + ' (ENDS) ' + code[p+3:p+5].encode('hex') + ' ' + code[p+5:].encode('hex'))
def parse_jumpnum(ins, allow_empty=False, err=error.STX): """ Parses a line number pointer as in GOTO, GOSUB, LIST, RENUM, EDIT, etc. """ if skip_white_read_if(ins, (tk.T_UINT, )): return vartypes.uint_to_value(bytearray(ins.read(2))) else: if allow_empty: return -1 # Syntax error raise error.RunError(err)
def parse_jumpnum(ins, allow_empty=False, err=error.STX): """ Parses a line number pointer as in GOTO, GOSUB, LIST, RENUM, EDIT, etc. """ if skip_white_read_if(ins, (tk.T_UINT,)): return vartypes.uint_to_value(bytearray(ins.read(2))) else: if allow_empty: return -1 # Syntax error raise error.RunError(err)
def bload(g, offset): """ Load a file into a block of memory. """ if g.read(1) != '\xfd': raise error.RunError(54) seg = vartypes.uint_to_value(bytearray(g.read(2))) foffset = vartypes.uint_to_value(bytearray(g.read(2))) if offset == None: offset = foffset # size. this gets ignored; even the \x1a at the end gets dumped onto the screen. vartypes.uint_to_value(bytearray(g.read(2))) buf = bytearray(g.read()) # remove any EOF marker at end if buf and buf[-1] == 0x1a: buf = buf[:-1] if tandy_syntax: buf = buf[:-7] g.close() addr = seg * 0x10 + offset set_memory_block(addr, buf)
def show_program(): """ Write a marked-up hex dump of the program to the log. """ code = state.basic_state.bytecode.getvalue() offset_val, p = 0, 0 for key in sorted(state.basic_state.line_numbers.keys())[1:]: offset, linum = code[p + 1:p + 3], code[p + 3:p + 5] last_offset = offset_val offset_val = vartypes.uint_to_value( bytearray(offset)) - program.program_memory_start linum_val = vartypes.uint_to_value(bytearray(linum)) logging.debug( (code[p:p + 1].encode('hex') + ' ' + offset.encode('hex') + ' (+%03d) ' + code[p + 3:p + 5].encode('hex') + ' [%05d] ' + code[p + 5:state.basic_state.line_numbers[key]].encode('hex')), offset_val - last_offset, linum_val) p = state.basic_state.line_numbers[key] logging.debug(code[p:p + 1].encode('hex') + ' ' + code[p + 1:p + 3].encode('hex') + ' (ENDS) ' + code[p + 3:p + 5].encode('hex') + ' ' + code[p + 5:].encode('hex'))
def parse_line_number(ins): """ Parse line number and leave pointer at first char of line. """ # if end of program or truncated, leave pointer at start of line number C0 DE or 00 00 off = ins.read(2) if off == '\0\0' or len(off) < 2: ins.seek(-len(off), 1) return -1 off = ins.read(2) if len(off) < 2: ins.seek(-len(off)-2, 1) return -1 else: return vartypes.uint_to_value(bytearray(off))
def parse_line_number(ins): """ Parse line number and leave pointer at first char of line. """ # if end of program or truncated, leave pointer at start of line number C0 DE or 00 00 off = ins.read(2) if off == '\0\0' or len(off) < 2: ins.seek(-len(off), 1) return -1 off = ins.read(2) if len(off) < 2: ins.seek(-len(off) - 2, 1) return -1 else: return vartypes.uint_to_value(bytearray(off))
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 __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 = '' self.access = 'RW' self.seg, self.offset, self.length = 0, 0, 0 if self.mode == 'O': self.write(devices.type_to_magic[filetype]) if self.filetype == 'M': self.write(vartypes.value_to_uint(seg) + vartypes.value_to_uint(offset) + vartypes.value_to_uint(length)) self.seg, self.offset, self.length = seg, offset, length else: # drop magic byte self.read_raw(1) if self.filetype == 'M': self.seg = vartypes.uint_to_value(bytearray(self.read(2))) self.offset = vartypes.uint_to_value(bytearray(self.read(2))) # size gets ignored: even the \x1a at the end is read self.length = vartypes.uint_to_value(bytearray(self.read(2)))
def __init__(self, fhandle, filetype, number, name, mode, seg, offset, length): """ Initialise program file object and write header. """ iolayer.RawFile.__init__(self, fhandle, filetype, mode) self.number = number # don't lock binary files self.lock = '' self.access = 'RW' self.seg, self.offset, self.length = 0, 0, 0 if self.mode == 'O': self.write(iolayer.type_to_magic[filetype]) if self.filetype == 'M': self.write(vartypes.value_to_uint(seg) + vartypes.value_to_uint(offset) + vartypes.value_to_uint(length)) self.seg, self.offset, self.length = seg, offset, length else: # drop magic byte self.read_raw(1) if self.filetype == 'M': self.seg = vartypes.uint_to_value(bytearray(self.read(2))) self.offset = vartypes.uint_to_value(bytearray(self.read(2))) # size gets ignored: even the \x1a at the end is read self.length = vartypes.uint_to_value(bytearray(self.read(2)))
def get_string_copy_packed(sequence): """ Return a packed copy of a string from its 3-byte sequence. """ length = ord(sequence[0:1]) address = vartypes.uint_to_value(sequence[-2:]) if address >= memory.var_start(): # string is stored in string space return state.basic_state.strings.copy_packed(sequence) else: # string is stored in code space or field buffers if address < memory.field_mem_start: return vartypes.pack_string('\0' * length) # find the file we're in start = address - memory.field_mem_start number = 1 + start // memory.field_mem_offset offset = start % memory.field_mem_offset try: return vartypes.pack_string(state.io_state.fields[number][offset:offset+length]) except KeyError, IndexError: return vartypes.pack_string('\0' * length)
def get_string_copy_packed(sequence): """ Return a packed copy of a string from its 3-byte sequence. """ length = ord(sequence[0:1]) address = vartypes.uint_to_value(sequence[-2:]) if address >= memory.var_start(): # string is stored in string space return state.basic_state.strings.copy_packed(sequence) else: # string is stored in code space or field buffers if address < memory.field_mem_start: return vartypes.pack_string('\0' * length) # find the file we're in start = address - memory.field_mem_start number = 1 + start // memory.field_mem_offset offset = start % memory.field_mem_offset try: return vartypes.pack_string( state.io_state.fields[number].buffer[offset:offset + length]) except KeyError, IndexError: return vartypes.pack_string('\0' * 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 = bytearray(state.basic_state.bytecode.read(2)) if len(next_addr) < 2 or next_addr == '\0\0': break next_addr = vartypes.uint_to_value(next_addr) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write(str(vartypes.value_to_uint(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 = bytearray(state.basic_state.bytecode.read(2)) if len(next_addr) < 2 or next_addr == '\0\0': break next_addr = vartypes.uint_to_value(next_addr) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write( str(vartypes.value_to_uint(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 oct_to_str(s): """ Convert oct token to Python string. """ return "&O" + oct(vartypes.uint_to_value(s))[1:]
def uint_to_str(s): """ Convert unsigned int token to Python string. """ return str(vartypes.uint_to_value(s))
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.value_to_uint(old_to_new[old_line]))) # 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) # write the indirect line numbers state.basic_state.bytecode.seek(0) while util.skip_to_read(state.basic_state.bytecode, ('\x0e', )) == '\x0e': # get the old g number jumpnum = vartypes.uint_to_value( bytearray(state.basic_state.bytecode.read(2))) try: newjump = old_to_new[jumpnum] except KeyError: # not redefined, exists in program? if jumpnum in state.basic_state.line_numbers: newjump = jumpnum else: linum = get_line_number(state.basic_state.bytecode.tell()) console.write_line('Undefined line ' + str(jumpnum) + ' in ' + str(linum)) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write(str(vartypes.value_to_uint(newjump))) # 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 record_to_sprite_size(self, byte_array): """ Read 4-byte record of sprite size. """ dx = vartypes.uint_to_value(byte_array[0:2]) / self.bitsperpixel dy = vartypes.uint_to_value(byte_array[2:4]) return dx, dy
def record_to_sprite_size_ega(self, byte_array): """ Read 4-byte record of sprite size in EGA modes. """ dx = vartypes.uint_to_value(byte_array[0:2]) dy = vartypes.uint_to_value(byte_array[2:4]) return dx, dy
def hex_to_str(s): """ Convert hex token to Python string. """ return "&H" + hex(vartypes.uint_to_value(s))[2:].upper()
def address(self, key): """ Return the address of a given key. """ return vartypes.uint_to_value(bytearray(key[-2:]))