#!/usr/bin/env python2 # 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. 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 self.sprite_priority = 0 # the ones that have bit value= 1 are in the back. 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" return self.char_ROM.read_memory(address & 0xFFF, size) #| (self.read_color_RAM(address) << 8) # Video_Matrix|Chargen|Sprite_Data_Pointers|Sprite_Data. if self.B_clip_address: address = address &~ (1 << 9) &~ (1 << 10) # FIXME does that also mappen with char_ROM? # FIXME return self. | (self.read_color_RAM(address) << 8) VIC_bank_offset = self.CIA2.VIC_bank * 16384 # TODO invalidate all the sprites once this changes. #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