def from_packed(cls, data): obj = cls() ret_addr_bytearray = bytearray([0, 0, 0, 0]) ret_addr_bytearray[1:] = bytearray(data[:3]) ret_addr_bytes = bytes(ret_addr_bytearray) obj.return_addr = struct.unpack('>I', ret_addr_bytes)[0] flags = six.indexbytes(data, 3) num_locals = flags & 15 if flags & 16: # discard return val obj.return_val_loc = None else: obj.return_val_loc = six.indexbytes(data, 4) # this should never have non-consecutive ones, right? # i.e. you can't have arg 3 without having args 1 and 2 (right?) args_flag = six.indexbytes(data, 5) obj.num_args = 0 for i in range(7): if args_flag >> i: obj.num_args += 1 used_stack_size = struct.unpack('>H', data[6:8])[0] obj.locals = [] for i in range(num_locals): addr = 8 + i * 2 local = struct.unpack('>H', data[addr:addr + 2])[0] obj.locals.append(local) obj.stack = [] for i in range(used_stack_size): addr = 8 + num_locals * 2 + i * 2 word = struct.unpack('>H', data[addr:addr + 2])[0] obj.stack.append(word) obj.size = 8 + num_locals * 2 + used_stack_size * 2 return obj
def from_packed(cls, data): obj = cls() ret_addr_bytearray = bytearray([0,0,0,0]) ret_addr_bytearray[1:] = bytearray(data[:3]) ret_addr_bytes = bytes(ret_addr_bytearray) obj.return_addr = struct.unpack('>I', ret_addr_bytes)[0] flags = six.indexbytes(data, 3) num_locals = flags & 15 if flags & 16: # discard return val obj.return_val_loc = None else: obj.return_val_loc = six.indexbytes(data, 4) # this should never have non-consecutive ones, right? # i.e. you can't have arg 3 without having args 1 and 2 (right?) args_flag = six.indexbytes(data, 5) obj.num_args = 0 for i in range(7): if args_flag >> i: obj.num_args += 1 used_stack_size = struct.unpack('>H', data[6:8])[0] obj.locals = [] for i in range(num_locals): addr = 8+i*2 local = struct.unpack('>H', data[addr:addr+2])[0] obj.locals.append(local) obj.stack = [] for i in range(used_stack_size): addr = 8+num_locals*2+i*2 word = struct.unpack('>H', data[addr:addr+2])[0] obj.stack.append(word) obj.size = 8+num_locals*2+used_stack_size*2 return obj
def print_table(env, opinfo): env.screen.finish_wrapping() tab_addr = opinfo.operands[0] width = opinfo.operands[1] if len(opinfo.operands) > 2: height = opinfo.operands[2] else: height = 1 if len(opinfo.operands) > 3: skip = opinfo.operands[3] else: skip = 0 col = env.cursor[env.current_window][1] for i in range(height): row = env.cursor[env.current_window][0] line = [ env.mem[tab_addr + i * (width + skip) + j] for j in range(width) ] write(env, zscii_to_ascii(env, line)) if i < height - 1: env.screen.finish_wrapping() if (env.current_window == 0 and row < env.hdr.screen_height_units - 1 or env.current_window == 1 and row < env.top_window_height - 1): env.cursor[env.current_window] = row + 1, col else: env.cursor[env.current_window] = row, col
def print_table(env, opinfo): env.screen.finish_wrapping() tab_addr = opinfo.operands[0] width = opinfo.operands[1] if len(opinfo.operands) > 2: height = opinfo.operands[2] else: height = 1 if len(opinfo.operands) > 3: skip = opinfo.operands[3] else: skip = 0 col = env.cursor[env.current_window][1] for i in range(height): row = env.cursor[env.current_window][0] line = [env.mem[tab_addr + i*(width+skip) + j] for j in range(width)] write(env, zscii_to_ascii(env, line)) if i < height - 1: env.screen.finish_wrapping() if (env.current_window == 0 and row < env.hdr.screen_height_units-1 or env.current_window == 1 and row < env.top_window_height-1): env.cursor[env.current_window] = row+1, col else: env.cursor[env.current_window] = row, col
def flush(self): self.finish_wrapping() term.home_cursor() buf = self.textBuf for i in range(len(buf)): for j in range(len(buf[i])): c = buf[i][j] write_char(c.char, c.fg_color, c.bg_color, c.text_style) if i < len(buf) - 1: write_char('\n', c.fg_color, c.bg_color, c.text_style) else: term.fill_to_eol_with_bg_color() term.flush()
def load_to_env(env, filename): msg = env.screen.msg try: subname, hdrChunk, memChunk, frames = read(filename) except IOError as ioerr: msg('error reading file: '+str(ioerr)+'\n') return False if subname != b'IFZS': msg('not a quetzal save file\n') if env.hdr.release != hdrChunk.release: msg('release doesn\'t match\n') elif env.hdr.serial != hdrChunk.serial: msg('serial doesn\'t match\n') elif env.hdr.checksum != hdrChunk.checksum: msg('checksum does\'t match\n') else: env.reset() for i in range(len(memChunk.mem)): if memChunk.compressed: env.mem[i] ^= six.indexbytes(memChunk.mem, i) else: env.mem[i] = six.indexbytes(memChunk.mem, i) env.fixup_after_restore() env.pc = hdrChunk.pc env.callstack = frames # pc is now in wrong place: # must fix based on z version # after this func returns! return True return False
def load_to_env(env, filename): msg = env.screen.msg try: subname, hdrChunk, memChunk, frames = read(filename) except IOError as ioerr: msg('error reading file: ' + str(ioerr) + '\n') return False if subname != b'IFZS': msg('not a quetzal save file\n') if env.hdr.release != hdrChunk.release: msg('release doesn\'t match\n') elif env.hdr.serial != hdrChunk.serial: msg('serial doesn\'t match\n') elif env.hdr.checksum != hdrChunk.checksum: msg('checksum does\'t match\n') else: env.reset() for i in range(len(memChunk.mem)): if memChunk.compressed: env.mem[i] ^= six.indexbytes(memChunk.mem, i) else: env.mem[i] = six.indexbytes(memChunk.mem, i) env.fixup_after_restore() env.pc = hdrChunk.pc env.callstack = frames # pc is now in wrong place: # must fix based on z version # after this func returns! return True return False
def scan_table(env, opinfo): val = opinfo.operands[0] tab_addr = opinfo.operands[1] tab_len = opinfo.operands[2] if len(opinfo.operands) > 3: form = opinfo.operands[3] else: form = 0x82 val_size = (form >> 7) + 1 # word or byte field_len = form & 127 addr = 0 for i in range(tab_len): test_addr = tab_addr + i*field_len if val_size == 2: test_val = env.u16(test_addr) else: test_val = env.mem[test_addr] if val == test_val: addr = test_addr break found = addr != 0 set_var(env, opinfo.store_var, addr) if found == opinfo.branch_on: handle_branch(env, opinfo.branch_offset) if DBG: warn(' found', found) warn(' addr', addr)
def fill_text_buffer(env, user_input, text_buffer): text_buf_len = env.mem[text_buffer] if text_buf_len < 2: err('read error: malformed text buffer') text_buf_ptr = text_buffer + 1 # TODO: make sure I'm interpreting this right. # Finding in test suites that you should be able # to edit the prefilled text, despite spec seeming # to say that any new input goes after prefilled # input. Maybe that directive includes ^H's? if env.hdr.version >= 5: text_buf_ptr += 1 max_len = text_buf_len-(text_buf_ptr-text_buffer) text_len = min(len(user_input), max_len) for i in range(text_len): env.write8(text_buf_ptr, user_input[i]) text_buf_ptr += 1 if env.hdr.version >= 5: env.write8(text_buffer + 1, text_buf_ptr-text_buffer-2) else: env.write8(text_buf_ptr, 0)
def scan_table(env, opinfo): val = opinfo.operands[0] tab_addr = opinfo.operands[1] tab_len = opinfo.operands[2] if len(opinfo.operands) > 3: form = opinfo.operands[3] else: form = 0x82 val_size = (form >> 7) + 1 # word or byte field_len = form & 127 addr = 0 for i in range(tab_len): test_addr = tab_addr + i * field_len if val_size == 2: test_val = env.u16(test_addr) else: test_val = env.mem[test_addr] if val == test_val: addr = test_addr break found = addr != 0 set_var(env, opinfo.store_var, addr) if found == opinfo.branch_on: handle_branch(env, opinfo.branch_offset) if DBG: warn(' found', found) warn(' addr', addr)
def blank_top_win(self): env = self.env term.home_cursor() for i in range(env.top_window_height): write_char('\n', env.fg_color, env.bg_color, env.text_style) self.textBuf[i] = self.make_screen_line() self.seenBuf[self.textBuf[i]] = False
def from_chunk(cls, chunk): obj = cls() obj.name, obj.size, obj.data = chunk.name, chunk.size, chunk.data num_resources = struct.unpack_from('>I', chunk.data)[0] obj.resources = [] for i in range(num_resources): usage, number, start = struct.unpack_from('>4sII', chunk.data[4+i*12:]) obj.resources.append(Resource(usage, number, start)) return obj
def clip_word_list(env, words): if env.hdr.version <= 3: MAX_WORD_LEN = 6 else: MAX_WORD_LEN = 9 for i in range(len(words)): if len(words[i]) > MAX_WORD_LEN: words[i] = words[i][:MAX_WORD_LEN] return words
def make_dict_string(env, text): if env.hdr.version >= 5 and env.hdr.alpha_tab_base: base = env.hdr.alpha_tab_base A0 = ''.join(map(chr, list(env.mem[base + 0 * 26:base + 1 * 26]))) A1 = ''.join(map(chr, list(env.mem[base + 1 * 26:base + 2 * 26]))) A2 = ''.join(map(chr, list(env.mem[base + 2 * 26:base + 3 * 26]))) else: A0 = Default_A0 A1 = Default_A1 A2 = Default_A2 if env.hdr.version == 1: A2 = Default_A2_for_z1 # TODO: S 3.7.1, which expects 4,5 for len-2 shift # seqs (not full lock) and works this way only for # dict lookups? Still unclear to me, can find no # examples of this. if env.hdr.version <= 3: KEY_LEN = 6 else: KEY_LEN = 9 text = text[:KEY_LEN] # will truncate again later, but shortens the loop ztext = [] for char in text: if char in A0: ztext.append(A0.index(char) + 6) elif char in A1: # only can be custom alphabets, no version 1/2 code needed ztext.append(4) ztext.append(A1.index(char) + 6) elif char in A2 and A2.index(char) != 0 and (env.hdr.version == 1 or A2.index(char) != 1): if env.hdr.version <= 2: ztext.append(3) else: ztext.append(5) ztext.append(A2.index(char) + 6) else: # 10-bit ZSCII (only 8 bits ever used) ztext.append(ord(char) >> 5) # top 3 bits ztext.append(ord(char) & 0x1f) # bottom 5 bits ztext = ztext[:KEY_LEN] # truncating multi-byte chars here while len(ztext) < KEY_LEN: ztext.append(5) packed_text = [] for i in range(0, len(ztext), 3): c, c1, c2 = ztext[i:i + 3] packed_text.append((c << 10) | (c1 << 5) | c2) packed_text[-1] |= 0x8000 return packed_text
def copy_table(env, opinfo): first = opinfo.operands[0] second = opinfo.operands[1] size = to_signed_word(opinfo.operands[2]) if second == 0: # zeros out first size = abs(size) for i in range(size): env.write8(first+i, 0) elif size > 0: # protects against corruption of overlapping tables tab = env.mem[first:first+size] for i in range(size): env.write8(second+i, tab[i]) elif size < 0: # allows for the corruption of overlapping tables size = abs(size) for i in range(size): env.write8(second+i, env.mem[first+i])
def copy_table(env, opinfo): first = opinfo.operands[0] second = opinfo.operands[1] size = to_signed_word(opinfo.operands[2]) if second == 0: # zeros out first size = abs(size) for i in range(size): env.write8(first + i, 0) elif size > 0: # protects against corruption of overlapping tables tab = env.mem[first:first + size] for i in range(size): env.write8(second + i, tab[i]) elif size < 0: # allows for the corruption of overlapping tables size = abs(size) for i in range(size): env.write8(second + i, env.mem[first + i])
def make_dict_string(env, text): if env.hdr.version >= 5 and env.hdr.alpha_tab_base: base = env.hdr.alpha_tab_base A0 = ''.join(map(chr, list(env.mem[base+0*26:base+1*26]))) A1 = ''.join(map(chr, list(env.mem[base+1*26:base+2*26]))) A2 = ''.join(map(chr, list(env.mem[base+2*26:base+3*26]))) else: A0 = Default_A0 A1 = Default_A1 A2 = Default_A2 if env.hdr.version == 1: A2 = Default_A2_for_z1 # TODO: S 3.7.1, which expects 4,5 for len-2 shift # seqs (not full lock) and works this way only for # dict lookups? Still unclear to me, can find no # examples of this. if env.hdr.version <= 3: KEY_LEN = 6 else: KEY_LEN = 9 text = text[:KEY_LEN] # will truncate again later, but shortens the loop ztext = [] for char in text: if char in A0: ztext.append(A0.index(char)+6) elif char in A1: # only can be custom alphabets, no version 1/2 code needed ztext.append(4) ztext.append(A1.index(char)+6) elif char in A2 and A2.index(char) != 0 and (env.hdr.version == 1 or A2.index(char) != 1): if env.hdr.version <= 2: ztext.append(3) else: ztext.append(5) ztext.append(A2.index(char)+6) else: # 10-bit ZSCII (only 8 bits ever used) ztext.append(ord(char) >> 5) # top 3 bits ztext.append(ord(char) & 0x1f) # bottom 5 bits ztext = ztext[:KEY_LEN] # truncating multi-byte chars here while len(ztext) < KEY_LEN: ztext.append(5) packed_text = [] for i in range(0, len(ztext), 3): c, c1, c2 = ztext[i:i+3] packed_text.append((c << 10) | (c1 << 5) | c2) packed_text[-1] |= 0x8000 return packed_text
def from_env(cls, env): obj = cls() obj.name = b'CMem' obj.mem = env.mem[:env.hdr.static_mem_base] for i in range(len(obj.mem)): obj.mem[i] ^= six.indexbytes(env.orig_mem, i) while obj.mem[-1] == 0: obj.mem.pop() obj.mem = bytes(bytearray(obj.mem)) obj.compressed = True return obj
def print_prop_list(env, obj): warn(' ',obj,'-',get_obj_str(env, obj)+':') ptr = get_prop_list_start(env, obj) while env.mem[ptr]: num = get_prop_num(env, ptr) size = get_prop_size(env, ptr) data_ptr = get_prop_data_ptr(env, ptr) warn(' prop #',num,' - size',size, end='') for i in range(size): warn(' ',hex(env.mem[data_ptr+i]), end='') warn() ptr = data_ptr + size
def dbg_dump_dictionary(env): dict_base = env.hdr.dict_base num_word_seps = env.mem[dict_base] entry_length = env.mem[dict_base + 1 + num_word_seps] num_entries = env.u16(dict_base + 1 + num_word_seps + 1) entries_start = dict_base + 1 + num_word_seps + 1 + 2 env.screen.write('\n') for i in range(num_entries): entry_addr = entries_start + i * entry_length entry = [env.u16(entry_addr), env.u16(entry_addr + 2)] entry_unpacked = ops.unpack_string(env, entry, warn_unknown_char=False) raw_hex = ' '.join(map(hex, entry)) env.screen.write(raw_hex + ' ' + repr(entry_unpacked) + '\n') env.screen.flush()
def dbg_dump_dictionary(env): dict_base = env.hdr.dict_base num_word_seps = env.mem[dict_base] entry_length = env.mem[dict_base+1+num_word_seps] num_entries = env.u16(dict_base+1+num_word_seps+1) entries_start = dict_base+1+num_word_seps+1+2 env.screen.write('\n') for i in range(num_entries): entry_addr = entries_start+i*entry_length entry = [env.u16(entry_addr), env.u16(entry_addr+2)] entry_unpacked = ops.unpack_string(env, entry, warn_unknown_char=False) raw_hex = ' '.join(map(hex, entry)) env.screen.write(raw_hex + ' ' + repr(entry_unpacked) + '\n') env.screen.flush()
def verify(env, opinfo): vsum = 0 for i in range(0x40, get_file_len(env)): vsum += six.indexbytes(env.orig_mem, i) vsum &= 0xffff result = vsum == env.hdr.checksum if result == opinfo.branch_on: handle_branch(env, opinfo.branch_offset) if DBG: warn(' vsum', vsum) warn(' checksum in header', env.hdr.checksum) warn(' branch_on', opinfo.branch_on) warn(' result', result)
def parse_call_header(env, call_addr): num_locals = env.mem[call_addr] if num_locals > 15: err('calling a non-function (more than 15 local vars)') if env.hdr.version < 5: locals_ptr = call_addr + 1 locals = [] for i in range(num_locals): locals.append(env.u16(locals_ptr)) locals_ptr += 2 code_ptr = locals_ptr else: locals = [0] * num_locals code_ptr = call_addr + 1 return locals, code_ptr
def output_stream(env, opinfo): stream = to_signed_word(opinfo.operands[0]) if stream < 0: stream = abs(stream) if stream == 3: table_addr = env.memory_ostream_stack.pop() zscii_buffer = ascii_to_zscii(env.output_buffer[stream]) buflen = len(zscii_buffer) env.write16(table_addr, buflen) for i in range(len(zscii_buffer)): env.write8(table_addr + 2 + i, zscii_buffer[i]) env.output_buffer[stream] = '' if len(env.memory_ostream_stack) == 0: env.selected_ostreams.discard(stream) else: env.selected_ostreams.discard(stream) elif stream > 0: env.selected_ostreams.add(stream) if stream == 3: table_addr = opinfo.operands[1] if len(env.memory_ostream_stack) == 16: err('too many memory-based ostreams (>16)') env.memory_ostream_stack.append(table_addr)
def output_stream(env, opinfo): stream = to_signed_word(opinfo.operands[0]) if stream < 0: stream = abs(stream) if stream == 3: table_addr = env.memory_ostream_stack.pop() zscii_buffer = ascii_to_zscii(env.output_buffer[stream]) buflen = len(zscii_buffer) env.write16(table_addr, buflen) for i in range(len(zscii_buffer)): env.write8(table_addr+2+i, zscii_buffer[i]) env.output_buffer[stream] = '' if len(env.memory_ostream_stack) == 0: env.selected_ostreams.discard(stream) else: env.selected_ostreams.discard(stream) elif stream > 0: env.selected_ostreams.add(stream) if stream == 3: table_addr = opinfo.operands[1] if len(env.memory_ostream_stack) == 16: err('too many memory-based ostreams (>16)') env.memory_ostream_stack.append(table_addr)
def decode(env, pc): opcode = env.mem[pc] form = get_opcode_form(env, opcode) count = get_operand_count(opcode, form) if form == ExtForm: opcode = env.mem[pc+1] if form == ShortForm: szbyte = (opcode >> 4) & 3 szbyte = (szbyte << 6) | 0x3f operand_ptr = pc+1 sizes = get_operand_sizes(szbyte) elif form == VarForm: szbyte = env.mem[pc+1] operand_ptr = pc+2 sizes = get_operand_sizes(szbyte) # handle call_vn2/vs2's extra szbyte if opcode in (236, 250): szbyte2 = env.mem[pc+2] sizes += get_operand_sizes(szbyte2) operand_ptr = pc+3 elif form == ExtForm: szbyte = env.mem[pc+2] operand_ptr = pc+3 sizes = get_operand_sizes(szbyte) elif form == LongForm: operand_ptr = pc+1 sizes = [] for offset in (6,5): if (opcode >> offset) & 1: sizes.append(VarSize) else: sizes.append(ByteSize) else: err('unknown opform specified: ' + str(form)) operands = [] var_op_info = [] for i in range(len(sizes)): size = sizes[i] if size == WordSize: operands.append(env.u16(operand_ptr)) operand_ptr += 2 elif size == ByteSize: operands.append(env.mem[operand_ptr]) operand_ptr += 1 elif size == VarSize: operands.append(None) #this is fixedup after every load from icache var_num = env.mem[operand_ptr] var_op_info.append( (i,var_num) ) operand_ptr += 1 else: err('unknown operand size specified: ' + str(size)) if form == ExtForm: dispatch = ops.ext_dispatch has_store_var = ops.ext_has_store_var has_branch_var = ops.ext_has_branch_var else: dispatch = ops.dispatch has_store_var = ops.has_store_var has_branch_var = ops.has_branch_var opinfo = OpInfo(operands, var_op_info) opinfo.opcode = opcode opinfo.is_extended = form == ExtForm if has_store_var[opcode]: opinfo.store_var = env.mem[operand_ptr] opinfo.last_pc_store_var = operand_ptr # to make quetzal saves easier operand_ptr += 1 if has_branch_var[opcode]: # std:4.7 branch_info = env.mem[operand_ptr] opinfo.last_pc_branch_var = operand_ptr # to make quetzal saves easier operand_ptr += 1 opinfo.branch_on = (branch_info & 128) == 128 if branch_info & 64: opinfo.branch_offset = branch_info & 0x3f else: branch_offset = branch_info & 0x3f branch_offset <<= 8 branch_offset |= env.mem[operand_ptr] operand_ptr += 1 # sign extend 14b # to 16b if branch_offset & 0x2000: branch_offset |= 0xc000 opinfo.branch_offset = to_signed_word(branch_offset) # handle print_ and print_ret's string operand if form != ExtForm and opcode in (178, 179): while True: word = env.u16(operand_ptr) operand_ptr += 2 operands.append(word) if word & 0x8000: break # After all that, operand_ptr should point to the next opcode next_pc = operand_ptr if DBG: def hex_out(bytes): s = '' for b in bytes: s += hex(b) + ' ' return s op_hex = hex_out(env.mem[pc:next_pc]) warn('decode: pc', hex(pc)) warn(' opcode', opcode) warn(' form', form) warn(' count', count) if opinfo.store_var: warn(' store_var', ops.get_var_name(opinfo.store_var)) warn(' sizes', sizes) warn(' operands', opinfo.operands) warn(' next_pc', hex(next_pc)) #warn(' bytes', op_hex) return dispatch[opcode], opinfo, next_pc
def handle_parse(env, text_buffer, parse_buffer, dict_base=0, skip_unknown_words=0): used_tbuf_len = get_used_tbuf_len(env, text_buffer) parse_buf_len = env.mem[parse_buffer] if parse_buf_len < 1: err('read error: malformed parse buffer') word_separators = [] if dict_base == 0: dict_base = env.hdr.dict_base num_word_seps = env.mem[dict_base] for i in range(num_word_seps): word_separators.append(env.mem[dict_base+1+i]) word = [] words = [] word_locs = [] word_len = 0 word_lens = [] scan_ptr = get_text_scan_ptr(env, text_buffer) for i in range(used_tbuf_len): c = env.mem[scan_ptr] if c == ord(' '): if word: word_lens.append(word_len) word_len = 0 words.append(word) word = [] scan_ptr += 1 elif c in word_separators: if word: word_lens.append(word_len) word_len = 0 words.append(word) word = [] word_locs.append(scan_ptr-text_buffer) word_lens.append(1) words.append([c]) scan_ptr += 1 else: if not word: word_locs.append(scan_ptr-text_buffer) word.append(c) word_len += 1 scan_ptr += 1 if word: word_lens.append(word_len) words.append(word) words = clip_word_list(env, words) # limit to parse_buf_len (which is num words) words = words[:parse_buf_len] word_locs = word_locs[:parse_buf_len] word_lens = word_lens[:parse_buf_len] # NOTE: This could be a binary search for read() opcodes, # but dictionaries for tokenize() can be unsorted. So maybe # just always do a linear search if speed is never an issue # these days? dict_base = env.hdr.dict_base num_word_seps = env.mem[dict_base] entry_length = env.mem[dict_base+1+num_word_seps] num_entries = env.u16(dict_base+1+num_word_seps+1) # this can be negative to signify dictionary is unsorted num_entries = abs(num_entries) entries_start = dict_base+1+num_word_seps+1+2 env.write8(parse_buffer+1, len(words)) parse_ptr = parse_buffer+2 for word,wloc,wlen in zip(words, word_locs, word_lens): wordstr = ''.join(map(chr, word)) packed_word = make_dict_string(env, wordstr) dict_addr = 0 for i in range(num_entries): entry_addr = entries_start+i*entry_length if match_dict_entry(env, entry_addr, packed_word): dict_addr = entry_addr break if dict_addr != 0 or skip_unknown_words == 0: env.write16(parse_ptr, dict_addr) env.write8(parse_ptr+2, wlen) env.write8(parse_ptr+3, wloc) parse_ptr += 4
def find_char_or_return_len(cs, c): for i in range(len(cs)): if cs[i].char == c: return i return len(cs)
def blank_bottom_win(self): for i in range(self.env.top_window_height, self.env.hdr.screen_height_units): self.scroll()
def decode(env, pc): opcode = env.mem[pc] form = get_opcode_form(env, opcode) count = get_operand_count(opcode, form) if form == ExtForm: opcode = env.mem[pc + 1] if form == ShortForm: szbyte = (opcode >> 4) & 3 szbyte = (szbyte << 6) | 0x3f operand_ptr = pc + 1 sizes = get_operand_sizes(szbyte) elif form == VarForm: szbyte = env.mem[pc + 1] operand_ptr = pc + 2 sizes = get_operand_sizes(szbyte) # handle call_vn2/vs2's extra szbyte if opcode in (236, 250): szbyte2 = env.mem[pc + 2] sizes += get_operand_sizes(szbyte2) operand_ptr = pc + 3 elif form == ExtForm: szbyte = env.mem[pc + 2] operand_ptr = pc + 3 sizes = get_operand_sizes(szbyte) elif form == LongForm: operand_ptr = pc + 1 sizes = [] for offset in (6, 5): if (opcode >> offset) & 1: sizes.append(VarSize) else: sizes.append(ByteSize) else: err('unknown opform specified: ' + str(form)) operands = [] var_op_info = [] for i in range(len(sizes)): size = sizes[i] if size == WordSize: operands.append(env.u16(operand_ptr)) operand_ptr += 2 elif size == ByteSize: operands.append(env.mem[operand_ptr]) operand_ptr += 1 elif size == VarSize: operands.append( None) #this is fixedup after every load from icache var_num = env.mem[operand_ptr] var_op_info.append((i, var_num)) operand_ptr += 1 else: err('unknown operand size specified: ' + str(size)) if form == ExtForm: dispatch = ops.ext_dispatch has_store_var = ops.ext_has_store_var has_branch_var = ops.ext_has_branch_var else: dispatch = ops.dispatch has_store_var = ops.has_store_var has_branch_var = ops.has_branch_var opinfo = OpInfo(operands, var_op_info) opinfo.opcode = opcode opinfo.is_extended = form == ExtForm if has_store_var[opcode]: opinfo.store_var = env.mem[operand_ptr] opinfo.last_pc_store_var = operand_ptr # to make quetzal saves easier operand_ptr += 1 if has_branch_var[opcode]: # std:4.7 branch_info = env.mem[operand_ptr] opinfo.last_pc_branch_var = operand_ptr # to make quetzal saves easier operand_ptr += 1 opinfo.branch_on = (branch_info & 128) == 128 if branch_info & 64: opinfo.branch_offset = branch_info & 0x3f else: branch_offset = branch_info & 0x3f branch_offset <<= 8 branch_offset |= env.mem[operand_ptr] operand_ptr += 1 # sign extend 14b # to 16b if branch_offset & 0x2000: branch_offset |= 0xc000 opinfo.branch_offset = to_signed_word(branch_offset) # handle print_ and print_ret's string operand if form != ExtForm and opcode in (178, 179): while True: word = env.u16(operand_ptr) operand_ptr += 2 operands.append(word) if word & 0x8000: break # After all that, operand_ptr should point to the next opcode next_pc = operand_ptr if DBG: def hex_out(bytes): s = '' for b in bytes: s += hex(b) + ' ' return s op_hex = hex_out(env.mem[pc:next_pc]) warn('decode: pc', hex(pc)) warn(' opcode', opcode) warn(' form', form) warn(' count', count) if opinfo.store_var: warn(' store_var', ops.get_var_name(opinfo.store_var)) warn(' sizes', sizes) warn(' operands', opinfo.operands) warn(' next_pc', hex(next_pc)) #warn(' bytes', op_hex) return dispatch[opcode], opinfo, next_pc
def make_screen_line(self): c, fg, bg, style = ' ', self.env.fg_color, self.env.bg_color, 'normal' return ScreenLine([ ScreenChar(c, fg, bg, style) for i in range(self.env.hdr.screen_width_units) ])
def first_draw(self): env = self.env for i in range(env.hdr.screen_height_units - 1): write_char('\n', env.fg_color, env.bg_color, env.text_style) term.fill_to_eol_with_bg_color() term.home_cursor()
def make_screen_buf(self): return [ self.make_screen_line() for i in range(self.env.hdr.screen_height_units) ]
def get_line_of_input(self, prompt='', prefilled=''): env = self.env for c in prompt: self.write_unwrapped( [ScreenChar(c, env.fg_color, env.bg_color, env.text_style)]) self.flush() self.update_seen_lines() row, col = env.cursor[env.current_window] term.home_cursor() term.cursor_down(row) term.cursor_right(col) term.set_color(env.fg_color, env.bg_color) if line_empty(self.textBuf[row][col:]): term.fill_to_eol_with_bg_color() term.show_cursor() col = max( 0, col - len(prefilled) ) # TODO: prefilled is a seldom-used old and crusty feature, but make unicode safe env.cursor[env.current_window] = row, col class CursorLine(object): def __init__(self, cursor_start, chars): self.cursor = cursor_start self.chars = chars def backspace(self): if self.cursor == 0: return self.left() self.delete_char() def delete_char(self): del self.chars[self.cursor:self.cursor + 1] self.refresh_rest_of_line(is_delete=True) def refresh_rest_of_line(self, is_delete=False): rest = self.chars[self.cursor:] term.puts(''.join(rest)) to_back_up = len(rest) if is_delete: term.puts(' ') to_back_up += 1 if to_back_up: term.cursor_left(to_back_up) def left(self): if self.cursor > 0: self.cursor -= 1 term.cursor_left() def right(self): if self.cursor < len(self.chars): self.cursor += 1 term.cursor_right() def home(self): while self.cursor > 0: self.left() def end(self): while self.cursor != len(self.chars): self.right() def kill_left(self): while self.chars[:self.cursor]: self.backspace() def kill_right(self): while self.chars[self.cursor:]: self.delete_char() def insert(self, c): self.chars.insert(self.cursor, c) term.puts(c) self.cursor += 1 self.refresh_rest_of_line() cursor_start = len(prefilled) cursor_line = CursorLine(cursor_start, [c for c in prefilled]) max_input_len = 120 # 120 char limit seen on gargoyle c = term.getch_or_esc_seq() while c != '\n' and c != '\r': if c == '\b' or c == '\x7f': cursor_line.backspace() # normal edit keys and a bit of readline flavor # left arrow or C-b elif c == '\x1b[D' or c == '\x02': cursor_line.left() # right arrow or C-f elif c == '\x1b[C' or c == '\x06': cursor_line.right() # home or C-a elif c == '\x1b[H' or c == '\x01': cursor_line.home() # end or C-e elif c == '\x1b[F' or c == '\x05': cursor_line.end() # delete or C-d elif c == '\x1b[3~' or c == '\x04': cursor_line.delete_char() # C-u, kill left of cursor elif c == '\x15': cursor_line.kill_left() # C-u, kill right of cursor elif c == '\x0b': cursor_line.kill_right() else: if is_valid_inline_char(c) and len( cursor_line.chars) < max_input_len: if c == '\t': if len(cursor_line.chars) + 4 <= max_input_len: for i in range(4): cursor_line.insert(' ') else: cursor_line.insert(c) c = term.getch_or_esc_seq() term.hide_cursor() term.flush() for c in cursor_line.chars: self.write_unwrapped( [ScreenChar(c, env.fg_color, env.bg_color, env.text_style)]) self.new_line_via_spaces(env.fg_color, env.bg_color, env.text_style) term.home_cursor() return ''.join(cursor_line.chars)