#!/usr/bin/env python
# I, Danny Milosavljevic, hereby place this file into the public domain.

# NES

import memory

class Settings(object):
    def __init__(self):
        self.B_display_enabled = True
        self.B_sprites_enabled = False
        self.background_color_0 = 0
        self.character_bitmaps_offset = 0
        self.video_offset = 0
        self.sprite_pattern_address = 0
        self.screen_pattern_address = 0
        self.sprite_height = 8

# 0x2000

A_CONTROL_0 = 0 # RW
A_CONTROL_1 = 1 # RW
A_STATUS = 2 # R
A_SPRITE_A_POINTER = 3 # W
A_SPRITE_A_DATA = 4 # RW # access increments [A_SPRITE_A_POINTER] by 1
A_SCREEN_SCROLL = 5 # W # 2 Bytes: vertical (ignored if >239), horizontal
A_PPU_A_POINTER = 6 # W # upper address byte, lower address byte.
A_PPU_A_DATA = 7 # RW # access increments [A_PPU_A_POINTER] (by 1 or 32). The first byte read will be invalid!
"""
------+-----+---------------------------------------------------------------
$2000 | RW  | PPU Control Register 1
      | 0-1 | Name Table Address:
      |     | 
      |     |           +-----------+-----------+
      |     |           | 2 ($2800) | 3 ($2C00) |
      |     |           +-----------+-----------+
      |     |           | 0 ($2000) | 1 ($2400) |
      |     |           +-----------+-----------+
      |     | 
      |     | Remember that because of the mirroring there are only 2
      |     | real Name Tables, not 4. Also, PPU will automatically
      |     | switch to another Name Table when running off the current
      |     | Name Table during scroll (see picture above).
      |   2 | Vertical Write, 1 = PPU memory address increments by 32:
      |     | 
      |     |    Name Table, VW=0          Name Table, VW=1
      |     |   +----------------+        +----------------+
      |     |   |----> write     |        | | write        |
      |     |   |                |        | V              |
      |     |
      |   3 | Sprite Pattern Table Address, 1 = $1000, 0 = $0000.
      |   4 | Screen Pattern Table Address, 1 = $1000, 0 = $0000.
      |   5 | Sprite Size, 1 = 8x16, 0 = 8x8.
      |   6 | PPU Master/Slave Mode, not used in NES.
      |   7 | VBlank Enable, 1 = generate interrupts on VBlank.
------+-----+---------------------------------------------------------------
$2001 | RW  | PPU Control Register 2
      |   0 | Unknown (???)
      |   1 | Image Mask, 0 = don't show left 8 columns of the screen.
      |   2 | Sprite Mask, 0 = don't show sprites in left 8 columns.
      |   3 | Screen Enable, 1 = show picture, 0 = blank screen.
      |   4 | Sprites Enable, 1 = show sprites, 0 = hide sprites.
      | 5-7 | Background Color, 0 = black, 1 = blue, 2 = green, 4 = red.
      |     | Do not use any other numbers as you may damage PPU hardware.
------+-----+---------------------------------------------------------------
$2002 | R   | PPU Status Register
      | 0-5 | Unknown (???)
      |   6 | Hit Flag, 1 = Sprite refresh has hit sprite #0.
      |     | This flag resets to 0 when screen refresh starts
      |     | (see "PPU Details").
      |   7 | VBlank Flag, 1 = PPU is in VBlank state.
      |     | This flag resets to 0 when VBlank ends or CPU reads $2002
      |     | (see "PPU Details").
------+-----+---------------------------------------------------------------
$2003 | W   | Sprite Memory Address
      |     | Used to set the address of the 256-byte Sprite Memory to be
      |     | accessed via $2004. This address will increment by 1 after
      |     | each access to $2004. Sprite Memory contains coordinates,
      |     | colors, and other sprite attributes (see "Sprites").
------+-----+---------------------------------------------------------------
$2004 | RW  | Sprite Memory Data
      |     | Used to read/write the Sprite Memory. The address is set via
      |     | $2003 and increments by 1 after each access. Sprite Memory
      |     | contains coordinates, colors, and other sprite attributes
      |     | sprites (see "Sprites").
------+-----+---------------------------------------------------------------
$2005 | W   | Screen Scroll Offsets
      |     | There are two scroll registers, vertical and horizontal,
      |     | which are both written via this port. The first value written
      |     | will go into the Vertical Scroll Register (unless it is >239,
      |     | then it will be ignored). The second value will appear in the
      |     | Horizontal Scroll Register. Name Tables are assumed to be
      |     | arranged in the following way:
      |     |
      |     |           +-----------+-----------+
      |     |           | 2 ($2800) | 3 ($2C00) |
      |     |           +-----------+-----------+
      |     |           | 0 ($2000) | 1 ($2400) |
      |     |           +-----------+-----------+
      |     |
      |     | When scrolled, the picture may span over several Name Tables.
      |     | Remember that because of the mirroring there are only 2 real
      |     | Name Tables, not 4.
------+-----+---------------------------------------------------------------
$2006 | W   | PPU Memory Address
      |     | Used to set the address of PPU Memory to be accessed via
      |     | $2007. The first write to this register will set 8 lower
      |     | address bits. The second write will set 6 upper bits. The
      |     | address will increment either by 1 or by 32 after each
      |     | access to $2007 (see "PPU Memory").
------+-----+---------------------------------------------------------------
$2007 | RW  | PPU Memory Data
      |     | Used to read/write the PPU Memory. The address is set via
      |     | $2006 and increments after each access, either by 1 or by 32
      |     | (see "PPU Memory").
------+-----+---------------------------------------------------------------
"""
# 8 bytes $2000..$2007 are PPU window in memory. See #screen for the actual device:
class PPU(memory.Memory):
    def __init__(self, toplevel, PPUMMU, screen):
        self.B_can_write = True
        self.MMU = PPUMMU
        self.screen = screen
        self.props = Settings()
        self.PPU_addr = 0 # FIXME default
        self.control_0 = 0
        self.control_1 = 0
        self.PPU_increment = 1
        self.sprite_increment = 1
        self.sprite_addr = 0
    def repaint(self):
        pass
    def unprepare(self):
        self.screen.unprepare()
    def update_timers(self):
        return(False)
    def increase_raster_position(self):
        self.screen.increase_raster_position()
        # TODO raster interrupt?
    def write_memory(self, address, value, size):
        assert size == 1, "PPU.write_memory: size==1" # since a lot of these things are actually instruction lists on the SAME address, it doesn't make much sense to have size > 1.
        if address == A_PPU_A_POINTER:
            self.PPU_addr = ((self.PPU_addr << 8) | value)&0xFFFF
        elif address == A_PPU_A_DATA:
            self.MMU.write_memory(self.PPU_addr, value, size)
            self.PPU_addr = (self.PPU_addr + self.PPU_increment) & 0xFFFF
        if address == A_SPRITE_A_POINTER:
            self.sprite_addr = ((self.sprite_addr << 8) | value)&0xFF
        elif address == A_SPRITE_A_DATA:
            self.screen.set_sprite_attribute(self.sprite_addr, value)
            self.sprite_addr = (self.sprite_addr + self.sprite_increment) & 0xFF
        elif address == A_CONTROL_0:
            self.control_0 = value
            self.PPU_increment = 32 if (value & 4) != 0 else 1
            self.props.video_offset = 0x2000 + 0x400*(value&3)
            self.props.sprite_pattern_address = 0x1000 if (value & 8) != 0 else 0
            self.props.screen_pattern_address = 0x1000 if (value & 16) != 0 else 0
            self.props.sprite_height = 16 if (value & 32) != 0 else 8
            # |   7 | VBlank Enable, 1 = generate interrupts on VBlank.
        elif address == A_CONTROL_1:
            self.control_1 = value
            #if value & 2: # Image Mask, 0 = don't show left 8 columns of the screen.
            #if value & 4: Sprite Mask, 0 = don't show sprites in left 8 columns.
            self.props.B_display_enabled = (value & 8) != 0
            self.props.B_sprites_enabled = (value & 16) != 0
            self.props.background_color_0 = (value>>5)&7 # 0=black,1=blue,2=green,4=red. Remainder doesn't work.
    def read_memory(self, address, size = 1):
        assert size == 1, "PPU.read_memory: size==1" # since a lot of these things are actually instruction lists on the SAME address, it doesn't make much sense to have size > 1.
        if address == A_PPU_A_DATA:
            # FIXME make the first byte read invalid
            value = self.MMU.read_memory(self.PPU_addr, size)
            self.PPU_addr = (self.PPU_addr + self.PPU_increment) & 0xFFFF
            return(value)
        elif address == A_SPRITE_A_DATA:
            # FIXME make the first byte read invalid
            value = self.screen.get_sprite_attribute(self.sprite_addr, size)
            self.PPU_addr = (self.sprite_addr + self.sprite_increment) & 0xFF
            return(value)
        else:
            return(0xFF)
