def get_next_prop(env, opinfo): obj = opinfo.operands[0] prop_num = opinfo.operands[1] next_prop_num = 0 if obj: if prop_num == 0: prop_start = get_prop_list_start(env, obj) next_prop_num = get_prop_num(env, prop_start) else: prop_data_ptr = get_prop_data_ptr_from_obj(env, obj, prop_num) if prop_data_ptr == 0: msg = 'get_next_prop: passed nonexistant prop ' msg += str(prop_num)+' for obj '+str(obj)+' ('+get_obj_str(env,obj)+')' print_prop_list(env, obj) err(msg) sizenum_ptr = get_sizenum_ptr(env, prop_data_ptr) size = get_prop_size(env, sizenum_ptr) next_prop_num = get_prop_num(env, prop_data_ptr + size) set_var(env, opinfo.store_var, next_prop_num) if DBG: warn(' prop_num', prop_num) warn(' next_prop_num', next_prop_num) print_prop_list(env, obj)
def get_next_prop(env, opinfo): obj = opinfo.operands[0] prop_num = opinfo.operands[1] next_prop_num = 0 if obj: if prop_num == 0: prop_start = get_prop_list_start(env, obj) next_prop_num = get_prop_num(env, prop_start) else: prop_data_ptr = get_prop_data_ptr_from_obj(env, obj, prop_num) if prop_data_ptr == 0: msg = 'get_next_prop: passed nonexistant prop ' msg += str(prop_num) + ' for obj ' + str( obj) + ' (' + get_obj_str(env, obj) + ')' print_prop_list(env, obj) err(msg) sizenum_ptr = get_sizenum_ptr(env, prop_data_ptr) size = get_prop_size(env, sizenum_ptr) next_prop_num = get_prop_num(env, prop_data_ptr + size) set_var(env, opinfo.store_var, next_prop_num) if DBG: warn(' prop_num', prop_num) warn(' next_prop_num', next_prop_num) print_prop_list(env, obj)
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 put_prop(env, opinfo): obj = opinfo.operands[0] prop_num = opinfo.operands[1] val = opinfo.operands[2] prop_data_ptr = get_prop_data_ptr_from_obj(env, obj, prop_num) if prop_data_ptr == 0: msg = 'illegal op: put_prop on nonexistant property' msg += ' - prop ' + str(prop_num) msg += ' not found on obj ' + str(obj) + ' (' + get_obj_str(env, obj) + ')' err(msg) sizenum_ptr = get_sizenum_ptr(env, prop_data_ptr) size = get_prop_size(env, sizenum_ptr) if size == 2: env.write16(prop_data_ptr, val) elif size == 1: env.write8(prop_data_ptr, val & 0xff) else: msg = 'illegal op: put_prop on outsized prop (not 1-2 bytes)' msg += ' - prop ' + str(prop_num) msg += ' of obj ' + str(obj) + ' (' + get_obj_str(obj) + ')' msg += ' (sized at ' + size + ' bytes)' err(msg) if DBG: warn(' obj', obj, '(', get_obj_str(env, obj), ')') warn(' prop_num', prop_num) warn(' val', val) print_prop_list(env, obj)
def get_prop(env, opinfo): obj = opinfo.operands[0] prop_num = opinfo.operands[1] prop_data_ptr = get_prop_data_ptr_from_obj(env, obj, prop_num) is_default_prop = prop_data_ptr == 0 if is_default_prop: base = env.hdr.obj_tab_base result = env.u16(base + 2 * (prop_num - 1)) else: sizenum_ptr = get_sizenum_ptr(env, prop_data_ptr) size = get_prop_size(env, sizenum_ptr) if size == 1: result = env.mem[prop_data_ptr] elif size == 2 or FORGIVING_GET_PROP: result = env.u16(prop_data_ptr) else: msg = 'illegal op: get_prop on outsized prop (not 1-2 bytes)' msg += ' - prop ' + str(prop_num) msg += ' of obj ' + str(obj) + ' (' + get_obj_str(env, obj) + ')' msg += ' (sized at ' + str(size) + ' bytes)' print_prop_list(env, obj) err(msg) set_var(env, opinfo.store_var, result) if DBG: warn(' obj', obj, '(', get_obj_str(env, obj), ')') warn(' prop_num', prop_num) warn(' result', result) warn(' is_default_prop', is_default_prop) print_prop_list(env, obj)
def get_prop(env, opinfo): obj = opinfo.operands[0] prop_num = opinfo.operands[1] prop_data_ptr = get_prop_data_ptr_from_obj(env, obj, prop_num) is_default_prop = prop_data_ptr == 0 if is_default_prop: base = env.hdr.obj_tab_base result = env.u16(base + 2*(prop_num-1)) else: sizenum_ptr = get_sizenum_ptr(env, prop_data_ptr) size = get_prop_size(env, sizenum_ptr) if size == 1: result = env.mem[prop_data_ptr] elif size == 2 or FORGIVING_GET_PROP: result = env.u16(prop_data_ptr) else: msg = 'illegal op: get_prop on outsized prop (not 1-2 bytes)' msg += ' - prop '+str(prop_num) msg += ' of obj '+str(obj)+' ('+get_obj_str(env, obj)+')' msg += ' (sized at '+str(size)+' bytes)' print_prop_list(env, obj) err(msg) set_var(env, opinfo.store_var, result) if DBG: warn(' obj', obj,'(',get_obj_str(env,obj),')') warn(' prop_num', prop_num) warn(' result', result) warn(' is_default_prop', is_default_prop) print_prop_list(env, obj)
def put_prop(env, opinfo): obj = opinfo.operands[0] prop_num = opinfo.operands[1] val = opinfo.operands[2] prop_data_ptr = get_prop_data_ptr_from_obj(env, obj, prop_num) if prop_data_ptr == 0: msg = 'illegal op: put_prop on nonexistant property' msg += ' - prop '+str(prop_num) msg += ' not found on obj '+str(obj)+' ('+get_obj_str(env, obj)+')' err(msg) sizenum_ptr = get_sizenum_ptr(env, prop_data_ptr) size = get_prop_size(env, sizenum_ptr) if size == 2: env.write16(prop_data_ptr, val) elif size == 1: env.write8(prop_data_ptr, val & 0xff) else: msg = 'illegal op: put_prop on outsized prop (not 1-2 bytes)' msg += ' - prop '+str(prop_num) msg += ' of obj '+str(obj)+' ('+get_obj_str(obj)+')' msg += ' (sized at '+size+' bytes)' err(msg) if DBG: warn(' obj', obj,'(',get_obj_str(env,obj),')') warn(' prop_num', prop_num) warn(' val', val) print_prop_list(env, obj)
def set_window(env, opinfo): env.screen.finish_wrapping() env.current_window = opinfo.operands[0] if env.current_window == 1: env.cursor[1] = (0,0) if env.current_window not in [0,1]: err('set_window: requested unknown window:', env.current_window)
def set_window(env, opinfo): env.screen.finish_wrapping() env.current_window = opinfo.operands[0] if env.current_window == 1: env.cursor[1] = (0, 0) if env.current_window not in [0, 1]: err('set_window: requested unknown window:', env.current_window)
def make_env(file): with open(file, 'rb') as f: mem = f.read() if blorb.is_blorb(mem): mem = blorb.get_code(mem) env = Env(mem, None) if env.hdr.version not in [1,2,3,4,5,7,8]: err('unsupported z-machine version: '+str(env.hdr.version)) return env
def read_char(env, opinfo): # NOTE: operands[0] must be 1, but I ran into a z5 that passed no operands # (strictz) so let's just ignore the first operand instead... if len(opinfo.operands) > 1: if len(opinfo.operands) != 3: err('read_char: num operands must be 1 or 3') if opinfo.operands[1] != 0 or opinfo.operands[2] != 0: if DBG: warn('read_char: interrupts not impl\'d yet!') c = ascii_to_zscii(env.screen.getch_or_esc_seq())[0] set_var(env, opinfo.store_var, c)
def get_code(filedata): formChunk = FormChunk.from_chunk(Chunk.from_data(filedata)) for chunk in formChunk.chunks: if chunk.name == b'RIdx': rIdxChunk = RIdxChunk.from_chunk(chunk) for r in rIdxChunk.resources: if r.usage == b'Exec' and r.number == 0: codeChunk = Chunk.from_data(filedata[r.start:]) if codeChunk.name == b'ZCOD': return codeChunk.data err('no ZCOD chunk found in blorb resource index')
def main(): if len(sys.argv) < 2: # I prefer a non-auto-gen'd zero arg screen if sys.argv[0].endswith('__main__.py'): name = '-m xyppy' else: name = sys.argv[0] print('usage examples:') print(' python ' + name + ' STORY_FILE.z5') print(' python ' + name + ' http://example.com/STORY_FILE.z5') print() print(' for more, try --help') sys.exit(1) parser = argparse.ArgumentParser() parser.add_argument('--no-slow-scroll', action='store_true', help='remove the artificial scrolling delay') parser.add_argument('STORY_FILE_OR_URL') args = parser.parse_args() url = args.STORY_FILE_OR_URL #This block of logic will create a save folder if needed, and switch into it. #This allows us to localize saves to where the game file is at. dir_of_game = "/".join(url.split("/")[:-1]) os.chdir(dir_of_game) if not os.path.exists("saves"): os.makedirs("saves") os.chdir("saves") if any(map(url.startswith, ['http://', 'https://', 'ftp://'])): f = urllib.request.urlopen(url) mem = f.read() f.close() else: with open(url, 'rb') as f: mem = f.read() if blorb.is_blorb(mem): mem = blorb.get_code(mem) env = Env(mem, args) if env.hdr.version not in [1, 2, 3, 4, 5, 7, 8]: err('unsupported z-machine version: ' + str(env.hdr.version)) term.init(env) env.screen.first_draw() ops.setup_opcodes(env) try: while True: step(env) except KeyboardInterrupt: pass
def set_colour(env, opinfo): fg_col = opinfo.operands[0] bg_col = opinfo.operands[1] if fg_col > 9 or bg_col > 9 or fg_col < 0 or bg_col < 0: err('set_color attempted illegal color') if fg_col == 1: fg_col = env.hdr.default_fg_color if fg_col != 0: env.fg_color = fg_col if bg_col == 1: bg_col = env.hdr.default_bg_color if bg_col != 0: env.bg_color = bg_col
def get_text_buffer_as_str(env, 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 # does input exist? if env.hdr.version >= 5 and env.mem[text_buf_ptr]: input_len = env.mem[text_buf_ptr] text_buf_ptr += 1 chars = env.mem[text_buf_ptr:text_buf_ptr+input_len] return ''.join(map(chr, chars)) return ''
def main(): if len(sys.argv) < 2: # I prefer a non-auto-gen'd zero arg screen if sys.argv[0].endswith('__main__.py'): name = '-m xyppy' else: name = sys.argv[0] print('usage examples:') print(' python ' + name + ' STORY_FILE.z5') print(' python ' + name + ' http://example.com/STORY_FILE.z5') print() print(' for more, try --help') sys.exit(1) parser = argparse.ArgumentParser() parser.add_argument('--no-slow-scroll', action='store_true', help='remove the artificial scrolling delay') parser.add_argument('STORY_FILE_OR_URL') args = parser.parse_args() url = args.STORY_FILE_OR_URL if any(map(url.startswith, ['http://', 'https://', 'ftp://'])): f = urllib.request.urlopen(url) mem = f.read() f.close() else: try: with open(url, 'rb') as f: mem = f.read() except IOError as e: err('could not load file:', e) if blorb.is_blorb(mem): mem = blorb.get_code(mem) env = Env(mem, args) if env.hdr.version not in [1, 2, 3, 4, 5, 7, 8]: err('unsupported z-machine version: ' + str(env.hdr.version)) term.init(env) env.screen.first_draw() ops.setup_opcodes(env) try: while True: step(env) except KeyboardInterrupt: pass
def get_prop_size(env, sizenum_ptr): if env.hdr.version < 4: return (env.mem[sizenum_ptr] >> 5) + 1 else: first_byte = env.mem[sizenum_ptr] if first_byte & 128: size_byte = env.mem[sizenum_ptr+1] if not (size_byte & 128): msg = 'malformed prop size byte: '+bin(size_byte) msg += ' - first_byte:'+bin(first_byte) msg += ' - sizenum_ptr:'+hex(sizenum_ptr) err(msg) return (size_byte & 63) or 64 # zero len == 64 if first_byte & 64: return 2 return 1
def handle_return(env, return_val): frame = env.callstack.pop() if frame.return_addr == 0: err('returned from unreturnable/nonexistant function!') if frame.return_val_loc != None: set_var(env, frame.return_val_loc, return_val) env.pc = frame.return_addr if DBG: warn(' helper: handle_return') warn(' return_val', return_val) if frame.return_val_loc: warn(' return_val_loc', get_var_name(frame.return_val_loc)) else: warn(' return_val_loc None') warn(' return_addr', hex(frame.return_addr))
def split_window(env, opinfo): env.screen.finish_wrapping() old_height = env.top_window_height # an unfortunate hack, but makes Inform games look better, # as they intentionally don't fill up the entire status bar (so # this is me trying to keep the Trinity trick and those bars both # looking good). only doing it on 0 to 1-bar transitions, # because those sound like status bars being made, right? if opinfo.operands[0] == 1 and env.top_window_height == 0: env.screen.scroll_top_line_only() env.top_window_height = opinfo.operands[0] if env.top_window_height > env.hdr.screen_height_units: err('split_window: requested split bigger than screen:', env.top_window_height)
def handle_read(env, text_buffer, parse_buffer, time=0, routine=0): if time != 0 or routine != 0: if DBG: err('interrupts requested but not impl\'d yet!') prefilled = get_text_buffer_as_str(env, text_buffer) user_input = ascii_to_zscii(env.screen.get_line_of_input(prompt='', prefilled=prefilled).lower()) fill_text_buffer(env, user_input, text_buffer) if env.hdr.version < 5 or parse_buffer != 0: handle_parse(env, text_buffer, parse_buffer) # return ord('\r') as term char for now... # TODO: the right thing return ord('\r')
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 handle_read(env, text_buffer, parse_buffer, time=0, routine=0): if time != 0 or routine != 0: if DBG: err('interrupts requested but not impl\'d yet!') prefilled = get_text_buffer_as_str(env, text_buffer) user_input = ascii_to_zscii( env.screen.get_line_of_input(prompt='', prefilled=prefilled).lower()) fill_text_buffer(env, user_input, text_buffer) if env.hdr.version < 5 or parse_buffer != 0: handle_parse(env, text_buffer, parse_buffer) # return ord('\r') as term char for now... # TODO: the right thing return ord('\r')
def get_var(env, var_num, pop_stack=True): # if DBG: # warn(' get_var(', get_var_name(var_num), ', pop_stack =', pop_stack, ')') if var_num == 0: frame = env.callstack[-1] if pop_stack: return frame.stack.pop() else: return frame.stack[-1] elif var_num < 16: frame = env.callstack[-1] return frame.locals[var_num - 1] elif var_num < 256: g_idx = var_num - 16 g_base = env.hdr.global_var_base return env.u16(g_base + 2 * g_idx) else: err('illegal var num: ' + str(var_num))
def get_var(env, var_num, pop_stack=True): # if DBG: # warn(' get_var(', get_var_name(var_num), ', pop_stack =', pop_stack, ')') if var_num == 0: frame = env.callstack[-1] if pop_stack: return frame.stack.pop() else: return frame.stack[-1] elif var_num < 16: frame = env.callstack[-1] return frame.locals[var_num - 1] elif var_num < 256: g_idx = var_num - 16 g_base = env.hdr.global_var_base return env.u16(g_base + 2*g_idx) else: err('illegal var num: '+str(var_num))
def main(): if len(sys.argv) < 2: # I prefer a non-auto-gen'd zero arg screen if sys.argv[0].endswith('__main__.py'): name = '-m xyppy' else: name = sys.argv[0] print('usage examples:') print(' python '+name+' STORY_FILE.z5') print(' python '+name+' http://example.com/STORY_FILE.z5') print() print(' for more, try --help') sys.exit(1) parser = argparse.ArgumentParser() parser.add_argument('--no-slow-scroll', action='store_true', help='remove the artificial scrolling delay') parser.add_argument('STORY_FILE_OR_URL') args = parser.parse_args() url = args.STORY_FILE_OR_URL if any(map(url.startswith, ['http://', 'https://', 'ftp://'])): f = urllib.request.urlopen(url) mem = f.read() f.close() else: with open(url, 'rb') as f: mem = f.read() if blorb.is_blorb(mem): mem = blorb.get_code(mem) env = Env(mem, args) if env.hdr.version not in [1,2,3,4,5,7,8]: err('unsupported z-machine version: '+str(env.hdr.version)) term.init(env) env.screen.first_draw() ops.setup_opcodes(env) try: while True: step(env) except KeyboardInterrupt: pass
def set_var(env, var_num, result, push_stack=True): # if DBG: # warn(' set_var(', get_var_name(var_num), ',', result, ', push_stack =', push_stack, ')') result &= 0xffff if var_num == 0: frame = env.callstack[-1] if push_stack: frame.stack.append(result) else: frame.stack[-1] = result elif var_num < 16: frame = env.callstack[-1] frame.locals[var_num - 1] = result elif var_num < 256: g_idx = var_num - 16 g_base = env.hdr.global_var_base env.write16(g_base + 2 * g_idx, result) else: err('set_var: illegal var_num: ' + str(var_num))
def set_var(env, var_num, result, push_stack=True): # if DBG: # warn(' set_var(', get_var_name(var_num), ',', result, ', push_stack =', push_stack, ')') result &= 0xffff if var_num == 0: frame = env.callstack[-1] if push_stack: frame.stack.append(result) else: frame.stack[-1] = result elif var_num < 16: frame = env.callstack[-1] frame.locals[var_num - 1] = result elif var_num < 256: g_idx = var_num - 16 g_base = env.hdr.global_var_base env.write16(g_base + 2*g_idx, result) else: err('set_var: illegal var_num: '+str(var_num))
def getch_impl(): # TODO: This windows impl keeps pipes/redirects from working. Need ReadFile for that, # with more complicated handling (personally, I'm just going to keep using unix/cygwin # for pipe-y debug stuff...) # TODO: Windows escape seqs via ReadConsoleInput, convert to VT100 seqs for more commonality. if is_windows: stdin_handle = ctypes.windll.kernel32.GetStdHandle(ctypes.c_ulong(-10)) one_char_buf = ctypes.c_uint32() chars_read = ctypes.c_uint32() # NOTE: W version of this function == ERROR_NOACCESS after text color set in photopia!? result = ctypes.windll.kernel32.ReadConsoleA( stdin_handle, ctypes.byref(one_char_buf), 1, ctypes.byref(chars_read), 0) if result == 0 or chars_read.value != 1: last_err = ctypes.windll.kernel32.GetLastError() print('LAST ERR', last_err) err('failed to read console') return chr(one_char_buf.value) else: #Unix return sys.stdin.read(1)
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 getch_impl(): # TODO: This windows impl keeps pipes/redirects from working. Need ReadFile for that, # with more complicated handling (personally, I'm just going to keep using unix/cygwin # for pipe-y debug stuff...) # TODO: Windows escape seqs via ReadConsoleInput, convert to VT100 seqs for more commonality. if is_windows: stdin_handle = ctypes.windll.kernel32.GetStdHandle(ctypes.c_ulong(-10)) one_char_buf = ctypes.c_uint32() chars_read = ctypes.c_uint32() # NOTE: W version of this function == ERROR_NOACCESS after text color set in photopia!? result = ctypes.windll.kernel32.ReadConsoleA(stdin_handle, ctypes.byref(one_char_buf), 1, ctypes.byref(chars_read), 0) if result == 0 or chars_read.value != 1: last_err = ctypes.windll.kernel32.GetLastError() print('LAST ERR', last_err) err('failed to read console') return chr(one_char_buf.value) else: #Unix return sys.stdin.read(1)
def check_dyn_mem(self, i): if i >= self.hdr.static_mem_base: err('game tried to write in static mem: ' + str(i)) elif i <= 0x36 and i != 0x10: err('game tried to write in non-dyn header bytes: ' + str(i))
def check_dyn_mem(self, i): if i >= self.hdr.static_mem_base: err('game tried to write in static mem: '+str(i)) elif i <= 0x36 and i != 0x10: err('game tried to write in non-dyn header bytes: '+str(i))
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 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