From 2250b4db92bd272dbb1fd717eb791e293c17e37a Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Thu, 29 Feb 2024 02:29:14 +0100 Subject: Store python modules on './bin/modules/' subdirectory. [#37] --- bin/world.py | 279 ----------------------------------------------------------- 1 file changed, 279 deletions(-) delete mode 100644 bin/world.py (limited to 'bin/world.py') diff --git a/bin/world.py b/bin/world.py deleted file mode 100644 index 08f2734..0000000 --- a/bin/world.py +++ /dev/null @@ -1,279 +0,0 @@ -""" SALIS: Viewer/controller for the SALIS simulator. - -File: world.py -Author: Paul Oliver -Email: paul.t.oliver.design@gmail.com - -This module should be considered an extension of the 'printer' module. It takes -care of getting a pre-redered image from Salis and post-processing it in order -to print it into the curses screen. It also keeps track of user controllable -rendering parameters (position and zoom). -""" - -import curses - -from ctypes import c_uint8, cast, POINTER - - -class World: - PADDING = 25 - - def __init__(self, printer, sim): - """ World constructor. We link to the printer and main simulation - classes. We also setup the colors for rendering the world. - """ - self.__printer = printer - self.__sim = sim - self.__set_world_colors() - self.__show_ip = True - self.pos = 0 - self.zoom = 1 - - def render(self): - """ Function for rendering the world. We get a pre-rendered buffer from - Salis' memory module (its way faster to pre-render in C) and use that - to assemble the world image in Python. - """ - # Window is so narrow that world is not visible. - if self.__printer.size[1] <= self.PADDING: - return - - # Get pre-rendered image from Salis' memory module. - line_width = self.__printer.size[1] - self.PADDING - print_area = self.__printer.size[0] * line_width - c_buffer = (c_uint8 * print_area)() - self.__sim.lib.sal_ren_get_image( - self.pos, self.zoom, print_area, cast(c_buffer, POINTER(c_uint8)) - ) - - # Get data elements of selected process, if it's running, and store - # them into a convenient dict object. - if self.__sim.lib.sal_proc_is_free(self.__printer.selected_proc): - sel_data = None - else: - out_data = self.__printer.selected_proc_data - out_elem = self.__printer.proc_elements - sel_data = { - "ip": out_data[out_elem.index("ip")], - "sp": out_data[out_elem.index("sp")], - "mb1a": out_data[out_elem.index("mb1a")], - "mb1s": out_data[out_elem.index("mb1s")], - "mb2a": out_data[out_elem.index("mb2a")], - "mb2s": out_data[out_elem.index("mb2s")], - } - - # Iterate all cells on printable area and print the post-rendered - # cells. Rendered cells contain info about bit flags and instructions - # currently written into memory. - bidx = 0 - - for y in range(self.__printer.size[0]): - for x in range(line_width): - xpad = x + self.PADDING - addr = self.pos + (self.zoom * bidx) - symb, attr = self.__render_cell(c_buffer[bidx], addr, sel_data) - - # Curses raises an exception when printing on the edge of the - # screen; we can just ignore it. - try: - self.__printer.screen.addstr(y, xpad, symb, attr) - except curses.error: - pass - - bidx += 1 - - def zoom_out(self): - """ Zoom out by a factor of 2 (zoom *= 2). - """ - if self.__is_world_editable(): - self.zoom = min(self.zoom * 2, self.__get_max_zoom()) - - def zoom_in(self): - """ Zoom in by a factor of 2 (zoom //= 2). - """ - if self.__is_world_editable(): - self.zoom = max(self.zoom // 2, 1) - - def zoom_reset(self): - """ Reset zoom to a valid value on certain events (i.e. during terminal - resizing). - """ - self.zoom = min(self.zoom, self.__get_max_zoom()) - - def pan_left(self): - """ Pan world to the left (pos -= zoom). - """ - if self.__is_world_editable(): - self.pos = max(self.pos - self.zoom, 0) - - def pan_right(self): - """ Pan world to the right (pos += zoom). - """ - if self.__is_world_editable(): - max_pos = self.__sim.lib.sal_mem_get_size() - 1 - self.pos = min(self.pos + self.zoom, max_pos) - - def pan_down(self, fast=False): - """ Pan world downward. - """ - if self.__is_world_editable(): - if fast: - self.pos = max(self.pos - self.__get_world_area(), 0) - else: - self.pos = max(self.pos - self.__get_line_area(), 0) - - def pan_up(self, fast=False): - """ Pan world upward. - """ - if self.__is_world_editable(): - max_pos = self.__sim.lib.sal_mem_get_size() - 1 - - if fast: - self.pos = min(self.pos + self.__get_world_area(), max_pos) - else: - self.pos = min(self.pos + self.__get_line_area(), max_pos) - - def pan_reset(self): - """ Set world position to zero. - """ - if self.__is_world_editable(): - self.pos = 0 - - def scroll_to(self, pos): - """ Move world pos to a specified position. - """ - if self.__is_world_editable(): - if self.__sim.lib.sal_mem_is_address_valid(pos): - self.pos = pos - else: - raise RuntimeError("Error: scrolling to an invalid address") - - def toggle_ip_view(self): - """ Turn on/off IP visualization. Turning off IPs might make it easier - to visualize the underlying memory block structure. - """ - if self.__is_world_editable(): - self.__show_ip = not self.__show_ip - - - ############################### - # Private methods - ############################### - - def __set_world_colors(self): - """ Define color pairs for rendering the world. Each color has a - special meaning, referring to the selected process IP, SP and memory - blocks, or to bit flags currently set on rendered cells. - """ - self.pair_free = self.__printer.get_color_pair( - curses.COLOR_BLUE - ) - self.pair_alloc = self.__printer.get_color_pair( - curses.COLOR_BLACK, curses.COLOR_BLUE - ) - self.pair_mbstart = self.__printer.get_color_pair( - curses.COLOR_BLACK, curses.COLOR_CYAN - ) - self.pair_ip = self.__printer.get_color_pair( - curses.COLOR_BLACK, curses.COLOR_WHITE - ) - self.pair_sel_mb2 = self.__printer.get_color_pair( - curses.COLOR_BLACK, curses.COLOR_GREEN - ) - self.pair_sel_mb1 = self.__printer.get_color_pair( - curses.COLOR_BLACK, curses.COLOR_YELLOW - ) - self.pair_sel_sp = self.__printer.get_color_pair( - curses.COLOR_BLACK, curses.COLOR_MAGENTA - ) - self.pair_sel_ip = self.__printer.get_color_pair( - curses.COLOR_BLACK, curses.COLOR_RED - ) - - def __render_cell(self, byte, addr, sel_data=None): - """ Render a single cell on the WORLD view. All cells are rendered by - interpreting the values coming in from the buffer. We overlay special - colors for representing the selected organism's state, on top of the - more common colors used to represent memory state. - """ - # Paint black all cells that are out of memory bounds. - if not self.__sim.lib.sal_mem_is_address_valid(addr): - return " ", curses.A_NORMAL - - # Check if cell contains part of the currently selected process. - if sel_data: - top_addr = addr + self.zoom - top_mb1a = sel_data["mb1a"] + sel_data["mb1s"] - top_mb2a = sel_data["mb2a"] + sel_data["mb2s"] - - if addr <= sel_data["ip"] < top_addr: - pair = self.pair_sel_ip - elif addr <= sel_data["sp"] < top_addr: - pair = self.pair_sel_sp - elif top_addr > sel_data["mb1a"] and top_mb1a > addr: - pair = self.pair_sel_mb1 - elif top_addr > sel_data["mb2a"] and top_mb2a > addr: - pair = self.pair_sel_mb2 - - # No pair has been selected yet; select pair based on bit-flags. - if not "pair" in locals(): - if self.__show_ip and byte >= 0x80: - pair = self.pair_ip - elif (byte % 0x80) >= 0x40: - pair = self.pair_mbstart - elif (byte % 0x40) >= 0x20: - pair = self.pair_alloc - else: - pair = self.pair_free - - # Select symbol to represent instructions currently on cell. - inst = byte % 32 - - if self.zoom == 1: - symb = self.__printer.inst_list[inst][1] - elif inst > 16: - symb = ":" - else: - symb = "." - - # Return tuple containing our post-redered cell. - return symb, curses.color_pair(pair) - - def __get_max_zoom(self): - """ Calculate maximum needed zoom so that the entire world fits on the - terminal window. - """ - max_zoom = 1 - line_size = self.__printer.size[1] - self.PADDING - coverage = self.__printer.size[0] * line_size - - # We fix a maximum zoom level; otherwise, program may halt on extreme - # zoom levels. - while ( - (coverage * max_zoom) < self.__sim.lib.sal_mem_get_size() and - max_zoom < 2 ** 16 - ): - max_zoom *= 2 - - return max_zoom - - def __is_world_editable(self): - """ For this to return True, printer's current page must be WORLD page. - Additionally, the WORLD panel must be visible on the terminal window - (i.e. curses.COLS > data_margin). - """ - correct_page = self.__printer.current_page == "WORLD" - correct_size = self.__printer.size[1] > self.PADDING - return correct_page and correct_size - - def __get_line_area(self): - """ Return amount of bytes contained in a printed WORLD line. - """ - line_size = self.__printer.size[1] - self.PADDING - line_area = self.zoom * line_size - return line_area - - def __get_world_area(self): - """ Return amount of bytes contained in the entire WORLD view. - """ - return self.__get_line_area() * self.__printer.size[0] -- cgit v1.2.1