diff --git a/README.md b/README.md index ea82bc9..a2fed2e 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ 4. [Clip Quirks](#clip-quirks) 5. [Logic Quirks](#logic-quirks) 5. [Memory Size](#memory-size) + 6. [Colors](#colors) 5. [Customization](#customization) 1. [Keys](#keys) 2. [Debug Keys](#debug-keys) @@ -304,6 +305,25 @@ but this behavior can be controlled with the `--mem_size` flag. Valid options ar `64K` or `4K` for historical purposes. +### Colors + +The original Chip8 language specification called for pixels to be turned on or +off. It did not specify what color the pixel states had to be. The emulator +lets the user specify what colors they want to use when the emulator is running. +Color values are specified by using HTML hex values such as `AABBCC` without the +leading `#`. There are currently 4 color values that can be set: + +* `--color_0` specifies the background color. This defaults to `000000`. +* `--color_1` specifies bitplane 1 color. This defaults to `666666`. +* `--color_2` specifies bitplane 2 color. This defaults to `BBBBBB`. +* `--color_3` specifies bitplane 1 and 2 overlap color. This defaults to `FFFFFF`. + +For Chip8 and SuperChip 8 programs, only the background color `color_0` (for pixels +turned off) and the bitplane 1 color `color_1` (for pixels turned on) are used. +Only XO Chip programs will use `color_2` and `color_3` when the additional bitplanes +are potentially used. + + ## Customization The file `chip8/config.py` contains several variables that can be changed to diff --git a/chip8/emulator.py b/chip8/emulator.py index 5e1c7cb..f751749 100644 --- a/chip8/emulator.py +++ b/chip8/emulator.py @@ -23,7 +23,13 @@ def main_loop(args): """ delay_timer_event = 24 - screen = Chip8Screen(scale_factor=args.scale) + screen = Chip8Screen( + scale_factor=args.scale, + color_0=args.color_0, + color_1=args.color_1, + color_2=args.color_2, + color_3=args.color_3, + ) screen.init_display() cpu = Chip8CPU( screen, diff --git a/chip8/screen.py b/chip8/screen.py index ecd3b81..162c300 100644 --- a/chip8/screen.py +++ b/chip8/screen.py @@ -18,13 +18,6 @@ # of a pixel. SCREEN_DEPTH = 8 -# The colors of the pixels to draw. The Chip 8 supports two colors: 0 (off) -# and 1 (on). The format of the colors is in RGBA format. -PIXEL_COLORS = { - 0: Color(0, 0, 0, 255), - 1: Color(250, 250, 250, 255) -} - # C L A S S E S ############################################################### @@ -34,7 +27,14 @@ class Chip8Screen: with 2 colors. In this emulator, this translates to color 0 (off) and color 1 (on). """ - def __init__(self, scale_factor): + def __init__( + self, + scale_factor, + color_0="000000", + color_1="666666", + color_2="BBBBBB", + color_3="FFFFFF" + ): """ Initializes the main screen. The scale factor is used to modify the size of the main screen, since the original resolution of the @@ -47,6 +47,12 @@ def __init__(self, scale_factor): self.scale_factor = scale_factor self.surface = None self.mode = SCREEN_MODE_NORMAL + self.pixel_colors = { + 0: Color(f"#{color_0}"), + 1: Color(f"#{color_1}"), + 2: Color(f"#{color_2}"), + 3: Color(f"#{color_3}"), + } def init_display(self): """ @@ -81,7 +87,7 @@ def draw_pixel(self, x_pos, y_pos, turn_on): x_base = (x_pos * mode_scale) * self.scale_factor y_base = (y_pos * mode_scale) * self.scale_factor draw.rect(self.surface, - PIXEL_COLORS[pixel_color], + self.pixel_colors[pixel_color], (x_base, y_base, mode_scale * self.scale_factor, mode_scale * self.scale_factor)) def get_pixel(self, x_pos, y_pos): @@ -97,7 +103,7 @@ def get_pixel(self, x_pos, y_pos): x_scale = (x_pos * mode_scale) * self.scale_factor y_scale = (y_pos * mode_scale) * self.scale_factor pixel_color = self.surface.get_at((x_scale, y_scale)) - return pixel_color == PIXEL_COLORS[1] + return pixel_color == self.pixel_colors[1] def get_width(self): """ @@ -119,7 +125,7 @@ def clear_screen(self): """ Turns off all the pixels on the screen (writes color 0 to all pixels). """ - self.surface.fill(PIXEL_COLORS[0]) + self.surface.fill(self.pixel_colors[0]) @staticmethod def update(): @@ -152,7 +158,7 @@ def scroll_down(self, num_lines): mode_scale = 1 if self.mode == SCREEN_MODE_EXTENDED else 2 actual_lines = num_lines * mode_scale * self.scale_factor self.surface.scroll(0, actual_lines) - self.surface.fill(PIXEL_COLORS[0], (0, 0, self.width * mode_scale * self.scale_factor, actual_lines)) + self.surface.fill(self.pixel_colors[0], (0, 0, self.width * mode_scale * self.scale_factor, actual_lines)) self.update() def scroll_left(self): @@ -163,7 +169,7 @@ def scroll_left(self): actual_lines = 4 * mode_scale * self.scale_factor left = (self.width * mode_scale * self.scale_factor) - actual_lines self.surface.scroll(-actual_lines, 0) - self.surface.fill(PIXEL_COLORS[0], (left, 0, actual_lines, self.height * mode_scale * self.scale_factor)) + self.surface.fill(self.pixel_colors[0], (left, 0, actual_lines, self.height * mode_scale * self.scale_factor)) self.update() def scroll_right(self): @@ -173,7 +179,7 @@ def scroll_right(self): mode_scale = 1 if self.mode == SCREEN_MODE_EXTENDED else 2 actual_lines = 4 * mode_scale * self.scale_factor self.surface.scroll(actual_lines, 0) - self.surface.fill(PIXEL_COLORS[0], (0, 0, actual_lines, self.height * mode_scale * self.scale_factor)) + self.surface.fill(self.pixel_colors[0], (0, 0, actual_lines, self.height * mode_scale * self.scale_factor)) self.update() # E N D O F F I L E ######################################################## diff --git a/yac8e.py b/yac8e.py index 8a03e9c..bcfa639 100755 --- a/yac8e.py +++ b/yac8e.py @@ -64,6 +64,22 @@ def parse_arguments(): "--trace", help="print registers and instructions to STDOUT", action="store_true", dest="trace" ) + parser.add_argument( + "--color_0", help="the hex color to use for the background (default=000000)", + dest="color_0", default="000000" + ) + parser.add_argument( + "--color_1", help="the hex color to use for bitplane 1 (default=666666)", + dest="color_1", default="666666" + ) + parser.add_argument( + "--color_2", help="the hex color to use for bitplane 2 (default=BBBBBB)", + dest="color_2", default="BBBBBB" + ) + parser.add_argument( + "--color_3", help="the hex color to use for bitplane overlaps (default=FFFFFF)", + dest="color_3", default="FFFFFF" + ) return parser.parse_args()