import subprocess NORMAL_COLOR = '\033[0m' REG_STATUS_TO_COLOR = { "insert": '\033[32m', "change": '\033[33m', "remove": '\033[31m', "none": '\033[0m', "jump": '\033[36m' } class AsciiPainter: def __init__ (self, registers=None, max_stack_size=8, stack_draw_mode="up"): self.registers = registers if registers else REGISTERS self.states = [] self.max_stack_size = max_stack_size self.stack_draw_mode = stack_draw_mode def saveState (self, emu, line_nr: int | None=None) -> None: # Init state state = [] self.states.append(state) # Write opcode to state if line_nr is not None: signature = emu.getLineSignature(line_nr) for token in signature: state.append({ 'state': 'none', 'val': token }) # for i in range(3-len(state)): state.append({'state':'none', 'val': ''}) for i in range(len(state)-3): state.pop() state.append({'state':'none', 'val':''}) # Write registers to state for reg_name in self.registers: if reg_name =="": state.append({'state':'none', 'val':''}) continue reg_state = emu.regState(reg_name) state.append({ 'state': reg_state, 'val': emu.getVal(reg_name) }) # Write stack to state stack_base_pointer = emu.getVal('%rbp') base_sp = emu.getVal('%rsp') assert(isinstance(emu.getVal('%rsp'), int)) state.append({'state':'none', 'val': ''}) if emu.regState('m'+str(base_sp-1)) == 'remove': state.append({'state': emu.regState('m'+str(base_sp-1)), 'val': emu.stack[base_sp-1]}) else: state.append({'state': 'none', 'val': ''}) for index in range(0, self.max_stack_size): stack_i = base_sp + index if stack_i < emu.max_stack_size: state.append({ 'state': emu.regState("m"+str(stack_i)), 'val': emu.stack[stack_i] }) if stack_base_pointer == index: stack_base_pointer = emu.stack[base_pointer] # TODO: Draw stack frame seperators else: state.append({'state':'none', 'val': ''}) def getWidthOfColumns(self, number_states: int, number_states_per_block: int, number_state_vars: int) -> list[int]: widths = [0 for i in range(0, number_states_per_block)] for base_state in range(0, number_states, number_states_per_block): for var_i in range(0, number_state_vars): for state_i in range(0, min(number_states_per_block, number_states - base_state)): width = len(str(self.states[base_state+state_i][var_i]['val'])) widths[state_i] = max(widths[state_i], width) return widths def getWidthOfNameColumn (self, number_state_vars: int) -> int: widest = 0 for var_i in range(0, number_state_vars): widest = max(widest, len(self.nameOfVar(var_i-4))) return widest def to_string (self, emu) -> str: number_states = len(self.states) number_state_vars = len(self.states[0]) term_width = int(subprocess.check_output(['tput', 'cols'])) number_states_per_block: int = term_width // (10+2) - 1 separator = '-' * term_width output: list[str] = [] name_fmt = '{}{:'+str(self.getWidthOfNameColumn(number_state_vars))+'} ' column_fmt = ['{}{:>'+str(width)+'} ' for width in self.getWidthOfColumns(number_states, number_states_per_block, number_state_vars)] for base_state in range(0, number_states, number_states_per_block): output.append(separator) for var_i in range(0, number_state_vars): output.append(name_fmt.format(NORMAL_COLOR, self.nameOfVar(var_i-4))) for state_i in range(0, min(number_states_per_block, number_states - base_state)): var = self.states[base_state+state_i][var_i] output.append(column_fmt[state_i].format(REG_STATUS_TO_COLOR[var['state']], var['val'], NORMAL_COLOR)) output.append('\n') output.append(separator) return ''.join(output) def nameOfVar (self, i): if i < 0: return '' if i < len(self.registers): return self.registers[i] elif i != len(self.registers): state_i = i-len(self.registers) - 2 return 'TOS{}{}'.format('+' if state_i < 0 else '-', abs(state_i)) else: return '' def __str__ (self): return 'AsciiPainter[]'