text: #!/usr/bin/env python2
  
  # Original: I, Danny Milosavljevic, hereby place this file
  #           into the public domain.
  
  # TODO The 47 registers of the VIC are mapped in at $d000. Due to the
  # incomplete address decoding, they are repeated every 64 bytes in the
  # area $d000-$d3ff.
  
  # VIC II video chip implementation . https://www.c64-wiki.com/index.php/VIC#VIC-II
  
  import sys
  #import time
  import memory
  import sprite
  from sprite import SPRITE_COUNT
  import palette
  import time
  import screens
  
  A_X_SPRITE_0 = 0x00
  A_Y_SPRITE_0 = 0x01
  A_X_SPRITE_1 = 0x02
  A_Y_SPRITE_1 = 0x03
  A_X_SPRITE_2 = 0x04
  A_Y_SPRITE_2 = 0x05
  A_X_SPRITE_3 = 0x06
  A_Y_SPRITE_3 = 0x07
  A_X_SPRITE_4 = 0x08
  A_Y_SPRITE_4 = 0x09
  A_X_SPRITE_5 = 0x0A
  A_Y_SPRITE_5 = 0x0B
  A_X_SPRITE_6 = 0x0C
  A_Y_SPRITE_6 = 0x0D
  A_X_SPRITE_7 = 0x0E
  A_Y_SPRITE_7 = 0x0F
  A_MSB_X = 0x10
  A_CONTROL_1 = 0x11
  A_RASTER_COUNTER = 0x12
  A_LIGHT_PEN_X = 0x13
  A_LIGHT_PEN_Y = 0x14
  A_SPRITE_ENABLED = 0x15  # bits
  A_CONTROL_2 = 0x16
  A_SPRITE_Y_EXPANSION = 0x17
  A_MEMORY_POINTERS = 0x18  # VM13..CB11, dummy bit (bits).
  A_INTERRUPT_STATUS = 0x19
  A_INTERRUPT_ENABLED = 0x1A
  A_SPRITE_DATA_PRIORITY = 0x1B
  A_SPRITE_MULTICOLOR = 0x1C
  A_SPRITE_X_EXPANSION = 0x1D
  A_SPRITE_SPRITE_COLLISION = 0x1E
  A_SPRITE_DATA_COLLISION = 0x1F
  A_BORDER_COLOR = 0x20
  A_BACKGROUND_COLOR_0 = 0x21
  A_BACKGROUND_COLOR_1 = 0x22
  A_BACKGROUND_COLOR_2 = 0x23
  A_BACKGROUND_COLOR_3 = 0x24
  A_SPRITE_MULTICOLOR_0 = 0x25
  A_SPRITE_MULTICOLOR_1 = 0x26
  A_COLOR_SPRITE_0 = 0x27
  A_COLOR_SPRITE_1 = 0x28
  A_COLOR_SPRITE_2 = 0x29
  A_COLOR_SPRITE_3 = 0x2A
  A_COLOR_SPRITE_4 = 0x2B
  A_COLOR_SPRITE_5 = 0x2C
  A_COLOR_SPRITE_6 = 0x2D
  A_COLOR_SPRITE_7 = 0x2E
  
  
  class Settings(object):
  
      def __init__(self):  # VIC):
          # self.VIC = VIC
          self.raw_memory_pointers = 0
          self.first_column = 0
          self.mode = 0
          self.first_row = 0
          self.last_column = 0
          self.last_row = 0
          self.character_bitmaps_offset = 0  # that's wrong.
          self.old_VIC_bank = -1
          self.VIC_bank = 0
          self.border_color = 0
          self.background_color_0 = 0
          self.background_color_1 = 0
          self.background_color_2 = 0
          self.background_color_3 = 0
          self.video_offset = 0
          self.viewport_row = 0
          # the ones that have bit value= 1 are in the back.
          self.sprite_priority = 0
          self.sprite_enabled = 0   # bitmask
          self.sprite_primary_color = SPRITE_COUNT * [0]
          self.sprite_multicolor_enabled = 0  # bitmask
          self.sprite_multicolor_0 = 0
          self.sprite_multicolor_1 = 0
          self.sprite_expand_horizontally = 0  # bitmask
          self.sprite_expand_vertically = 0    # bitmask
          self.sprite_X = SPRITE_COUNT * [0]
          self.sprite_Y = SPRITE_COUNT * [0]
          #self.controls = controls
          #self.controls.handle_key_press("foo")
          #self.controls.handle_key_release("bar")
  
      #def repaint(self):
      #    pass
  
      def unprepare(self):
          self.old_VIC_bank = -1
  
  
  class VIC_II(memory.Memory):
  
      def __init__(self, C64, MMU, CIA2, char_ROM):
          self.B_can_write = True  # in the instance because of ShedSkin
          self.MMU = MMU
          self.C64 = C64
          self.char_ROM = char_ROM
          self.CIA2 = CIA2
          self.B_active = True
          self.B_bitmap = False
          self.control_1 = 0
          self.control_2 = 0
          self.B_clip_address = False  # FIXME default?
          self.B_MCM = False  # FIXME default?
          self.props = Settings()
          self.MSB_X = 0
          #self.set_control_1(0)  # FIXME default.
          #self.set_control_2(0)  # FIXME default.
          palette.get_RGBA32_pixel(0)
          self.screen = screens.Screen(self, CIA2)
          self.screen.get_rendered_pixbuf()  # ShedSkin
  
      def increase_raster_position(self):
          self.screen.increase_raster_position()
  
          if (self.screen.raw_interrupt_status != 0 and
             self.screen.B_enable_raster_interrupt):
              self.C64.cause_interrupt()
  
          return True
  
      def unprepare(self):
          self.props.unprepare()
  
      def set_control_1(self, value):
          self.control_1 = value
          self.props.first_row = (0) + (51 if value & 8 else 55)  # set: 25 lines
          self.props.last_row = (0) + (250 if value & 8 else 246)
          self.props.viewport_row = value & 7
          value & 16  # DEN
          self.B_bitmap = (value & 32) != 0  # BMM
          self.B_clip_address = (value & 64) != 0  # ECM # bits 9 and 10 low.
          #value & 128 # RST8 # TODO this is also used for multicolor bitmap mode
          old_mode = self.props.mode
          self.props.mode = (self.props.mode & 4) | ((value >> 5) & 3)
          # self.props.bitmap_mode = (1 if self.B_bitmap else 0) + 2
          # * (1 if self.B_clip_address else 0)
          self.screen.breakpoint_raster_position = (
              self.screen.breakpoint_raster_position & 0xFF) | (value & 128)
  
          # if old_mode != self.props.mode:
          #   print("new mode is $%X" % self.props.mode)
              # time.sleep(10)
  
      def set_control_2(self, value):
          self.control_2 = value
          self.props.first_column = 24 if value & 8 else 31
          self.props.last_column = 343 if value & 8 else 334
          self.props.viewport_column = value & 7
          self.B_MCM = (value & 16) != 0
          old_mode = self.props.mode
          self.props.mode = (self.props.mode & 3) | (4 if self.B_MCM else 0)
          # if old_mode != self.props.mode:
          #   print("new mode is $%X" % self.props.mode)
              #time.sleep(10)
          # TODO 32, 64, 128
  
      def set_memory_pointers(self, value):
          self.raw_memory_pointers = value
          value >>= 1
          CB_13_12_11 = value & 0x7  # character bitmaps or bitmaps.
          # TODO in bitmap mode, CB_13 only! (thus 2KiB/8KiB steps).
          VM_13_12_11_10 = value >> 3  # video matrix (movable in 1KiB steps).
          self.props.character_bitmaps_offset = (1 << 11) * CB_13_12_11
          self.props.video_offset = (1 << 10) * VM_13_12_11_10
          self.props.unprepare()
          if False:  # ShedSkin
              code_color = self.VIC_read_memory(0, 1)  # ShedSkin
              character_data = self.load_chunk(0, 8 * 256)  # ShedSkin
              character_data = self.load_12_chunk(0, 8 * 256)  # ShedSkin
  
      def load_chunk(self, offset, size):
          #address = VIC_bank_offset + offset
          return [self.VIC_read_memory(offset + i, 1) for i in range(size)]
  
      def load_12_chunk(self, offset, size):
          #address = VIC_bank_offset + offset
          return [self.VIC_read_memory(offset + i, 2) for i in range(size)]
  
      def repaint(self):
          self.props.VIC_bank = self.CIA2.VIC_bank
          #self.props.repaint()
  
      def read_color_RAM(self, address):
          return self.MMU.read_memory(0xD800 + (address & 0x3FF))
  
      def VIC_read_memory(self, address, size=1):
          if (self.CIA2.VIC_bank & 1) == 0:  # have Char ROM
              if address >= 0x1000 and address < 0x2000:
                  assert size == 1, (
                      'VIC_II.VIC_read_memory: address within char ROM')
                  #| (self.read_color_RAM(address) << 8). follow below..
                  return self.char_ROM.read_memory(address & 0xFFF, size)
  
          # Video_Matrix|Chargen|Sprite_Data_Pointers|Sprite_Data.
  
          if self.B_clip_address:
              # FIXME does that also mappen with char_ROM?
              address = address &~ (1 << 9) &~ (1 << 10)
  
          # FIXME return self. | (self.read_color_RAM(address) << 8)
          # TODO invalidate all the sprites once this chages
          VIC_bank_offset = self.CIA2.VIC_bank * 16384
          # assert(size == 2)
          return self.MMU.read_memory((address & 0x3FFF)
              | VIC_bank_offset, 1) | (((self.read_color_RAM(address)
                & 0xFF) << 8) if size > 1 else 0)
  
      def set_background_color_0(self, value):
          self.props.background_color_0 = value & 15
  
      def set_background_color_1(self, value):
          self.props.background_color_1 = value & 15
  
      def set_background_color_2(self, value):
          self.props.background_color_2 = value & 15
  
      def set_background_color_3(self, value):
          self.props.background_color_3 = value & 15
  
      def set_sprite_priority(self, value):
          self.props.sprite_priority = value
  
      def set_sprite_enabled(self, value):
          self.props.sprite_enabled = value
  
      def set_sprite_multicolor_0(self, value):
          self.props.sprite_multicolor_0 = value
  
      def set_sprite_multicolor_1(self, value):
          self.props.sprite_multicolor_1 = value
  
      def set_border_color(self, value):
          self.props.border_color = value & 15
  
      def set_sprite_primary_color(self, index, value):
          self.props.sprite_primary_color[index] = value
  
      def set_sprite_multicolor_enabled(self, value):
          mask = self.props.sprite_multicolor_enabled ^ value
          self.props.sprite_multicolor_enabled = value
  
      def set_sprite_expand_horizontally(self, value):
          mask = self.props.sprite_expand_horizontally ^ value
          self.props.sprite_expand_horizontally = value
  
      def set_sprite_expand_vertically(self, value):
          mask = self.props.sprite_expand_vertically ^ value
          self.props.sprite_expand_vertically = value
  
      def set_sprite_X(self, index, value):
          self.props.sprite_X[index] = value | (256 * ((self.MSB_X &
            (1 << index)) and 1))
  
      def set_MSB_X(self, value):
          self.MSB_X = value
          for index in range(8):
              self.props.sprite_X[index] = (self.props.sprite_X[index]
                  & 0xFF) | (256 * ((self.MSB_X & (1 << index)) and 1))
  
      def set_sprite_Y(self, index, value):
          self.props.sprite_Y[index] = value
  
      def read_memory(self, address, size=1):
          assert size == 1, 'VIC_II.read_memory: size==1'
          address = address & 0x3F
          # TODO The registers $d01e and $d01f are automatically
          # cleared on reading.
          self.control_1 = (self.control_1 & 127) | (
              (self.screen.client_raster_position & 0x100) >> 1)
          slots = {
              A_BORDER_COLOR: self.props.border_color,
              A_BACKGROUND_COLOR_0: self.props.background_color_0,
              A_BACKGROUND_COLOR_1: self.props.background_color_1,
              A_BACKGROUND_COLOR_2: self.props.background_color_2,
              A_BACKGROUND_COLOR_3: self.props.background_color_3,
              A_RASTER_COUNTER: self.screen.client_raster_position & 0xFF,
              A_X_SPRITE_0: self.props.sprite_X[0] & 0xFF,
              A_Y_SPRITE_0: self.props.sprite_Y[0],
              A_X_SPRITE_1: self.props.sprite_X[1] & 0xFF,
              A_Y_SPRITE_1: self.props.sprite_Y[1],
              A_X_SPRITE_2: self.props.sprite_X[2] & 0xFF,
              A_Y_SPRITE_2: self.props.sprite_Y[2],
              A_X_SPRITE_3: self.props.sprite_X[3] & 0xFF,
              A_Y_SPRITE_3: self.props.sprite_Y[3],
              A_X_SPRITE_4: self.props.sprite_X[4] & 0xFF,
              A_Y_SPRITE_4: self.props.sprite_Y[4],
              A_X_SPRITE_5: self.props.sprite_X[5] & 0xFF,
              A_Y_SPRITE_5: self.props.sprite_Y[5],
              A_X_SPRITE_6: self.props.sprite_X[6] & 0xFF,
              A_Y_SPRITE_6: self.props.sprite_Y[6],
              A_X_SPRITE_7: self.props.sprite_X[7] & 0xFF,
              A_Y_SPRITE_7: self.props.sprite_Y[7],
              A_MSB_X: self.MSB_X,
              A_CONTROL_1: self.control_1,
              #A_LIGHT_PEN_X = 0x13
              #A_LIGHT_PEN_Y = 0x14
              A_SPRITE_ENABLED: self.props.sprite_enabled,
              A_CONTROL_2: self.control_2,
              A_SPRITE_Y_EXPANSION: self.props.sprite_expand_vertically,
              A_MEMORY_POINTERS: self.raw_memory_pointers,
              A_INTERRUPT_STATUS: self.screen.raw_interrupt_status,
              A_INTERRUPT_ENABLED: (
                1 if self.screen.B_enable_raster_interrupt else 0) |
                (2 if self.screen.B_enable_sprite_background_collision_interrupt
                  else 0) |
                (4 if self.screen.B_enable_sprite_sprite_collision_interrupt
                  else 0),
              A_SPRITE_DATA_PRIORITY: self.props.sprite_priority,
              A_SPRITE_MULTICOLOR: self.props.sprite_multicolor_enabled,
              A_SPRITE_X_EXPANSION: self.props.sprite_expand_horizontally,
              #A_SPRITE_SPRITE_COLLISION = 0x1E
              #A_SPRITE_DATA_COLLISION = 0x1F
              #A_BACKGROUND_COLOR_1 = 0x22
              #A_BACKGROUND_COLOR_2 = 0x23
              #A_BACKGROUND_COLOR_3 = 0x24
              A_SPRITE_MULTICOLOR_0: self.props.sprite_multicolor_0,
              A_SPRITE_MULTICOLOR_1: self.props.sprite_multicolor_1,
              A_COLOR_SPRITE_0: self.props.sprite_primary_color[0],
              A_COLOR_SPRITE_1: self.props.sprite_primary_color[1],
              A_COLOR_SPRITE_2: self.props.sprite_primary_color[2],
              A_COLOR_SPRITE_3: self.props.sprite_primary_color[3],
              A_COLOR_SPRITE_4: self.props.sprite_primary_color[4],
              A_COLOR_SPRITE_5: self.props.sprite_primary_color[5],
              A_COLOR_SPRITE_6: self.props.sprite_primary_color[6],
              A_COLOR_SPRITE_7: self.props.sprite_primary_color[7],
          }
          return slots[address] if address in slots else 0xFF
  
      def write_memory(self, address, value, size):
          assert isinstance(value, int), (
              'VIC_II.write_memory: value is an integer')
          # TODO The registers $d01e and $d01f cannot be written.
          address = address & 0x3F
          value = (value)
          # TODO 47 control registers.
          # 34 for sprite control.
          #print("VIC-II $%X := %r" % (address, value))
          #time.sleep(5)
          if address == A_CONTROL_1:
              return self.set_control_1(value)
          elif address == A_CONTROL_2:
              return self.set_control_2(value)
          elif address == A_MEMORY_POINTERS:
              return self.set_memory_pointers(value)
          elif address == A_BORDER_COLOR:
              return self.set_border_color(value)
          elif address == A_BACKGROUND_COLOR_0:
              return self.set_background_color_0(value)
          elif address == A_BACKGROUND_COLOR_1:
              return self.set_background_color_1(value)
          elif address == A_BACKGROUND_COLOR_2:
              return self.set_background_color_2(value)
          elif address == A_BACKGROUND_COLOR_3:
              return self.set_background_color_3(value)
          elif address == A_SPRITE_DATA_PRIORITY:
              return self.set_sprite_priority(value)
          elif address == A_SPRITE_ENABLED:
              return self.set_sprite_enabled(value)
          elif address == A_COLOR_SPRITE_0:
              return self.set_sprite_primary_color(0, value)
          elif address == A_COLOR_SPRITE_1:
              return self.set_sprite_primary_color(1, value)
          elif address == A_COLOR_SPRITE_2:
              return self.set_sprite_primary_color(2, value)
          elif address == A_COLOR_SPRITE_3:
              return self.set_sprite_primary_color(3, value)
          elif address == A_COLOR_SPRITE_4:
              return self.set_sprite_primary_color(4, value)
          elif address == A_COLOR_SPRITE_5:
              return self.set_sprite_primary_color(5, value)
          elif address == A_COLOR_SPRITE_6:
              return self.set_sprite_primary_color(6, value)
          elif address == A_COLOR_SPRITE_7:
              return self.set_sprite_primary_color(7, value)
          elif address == A_SPRITE_MULTICOLOR:
              return self.set_sprite_multicolor_enabled(value)
          elif address == A_SPRITE_MULTICOLOR_0:
              return self.set_sprite_multicolor_0(value)
          elif address == A_SPRITE_MULTICOLOR_1:
              return self.set_sprite_multicolor_1(value)
          elif address == A_SPRITE_X_EXPANSION:
              return self.set_sprite_expand_horizontally(value)
          elif address == A_SPRITE_Y_EXPANSION:
              return self.set_sprite_expand_vertically(value)
          elif address == A_RASTER_COUNTER:
              self.screen.breakpoint_raster_position = (
                  self.screen.breakpoint_raster_position & 0x100) | value
          elif address == A_INTERRUPT_STATUS:
              self.screen.raw_interrupt_status = (
                  self.screen.raw_interrupt_status & (value ^ 0xFF))
          elif address == A_INTERRUPT_ENABLED:
              self.screen.B_enable_raster_interrupt = (value & 1) != 0
              self.screen.B_enable_sprite_background_collision_interrupt = (
                  value & 2) != 0
              self.screen.B_enable_sprite_sprite_collision_interrupt = (
                  value & 4) != 0
              # TODO light pen
          elif address < 0x10:  # coordinates
              if address & 1:
                  return self.set_sprite_Y(address >> 1, value)
              else:
                  return self.set_sprite_X(address >> 1, value)
          elif address == A_MSB_X:
              return self.set_MSB_X(value)
          #}.get(address) or ignore)(value)
  
  """
  [$11]=$1B, [$16]=$8: hires text mode (global bg in $21).
  [$11]=$1B, [$16]=216: multicolor text mode.
  [$11]=$3B, [$16]=8: hires bitmap mode.
  [$11]=$3B, [$16]=216: multicolor bitmap mode.
  [$11]=$5B, [$16]=8: extended (background color) text mode.
  [$16]=5: !!!
  http://codebase64.org/doku.php?id=base:built_in_screen_modes
  """
  # memory address $D02F (extra keys). Try to set to something
  # else than $FF. If it works, it's a C128.
  if __name__ == '__main__':
      pass
  