aboutsummaryrefslogtreecommitdiff
path: root/bin/world.py
diff options
context:
space:
mode:
Diffstat (limited to 'bin/world.py')
-rw-r--r--bin/world.py279
1 files changed, 0 insertions, 279 deletions
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]