#!/usr/bin/env python
# I, Danny Milosavljevic, hereby place this file into the public domain.

import memory
import ram

# use D600 and $D601 (in bank 15) to communicate (indirectly).
# http://www.c64-wiki.de/index.php/VDC
# 16 KiB dedicated RAM for the VDC (64 KiB for C128DCR)
# resolution 640x200 or 800x240 or 800x480(interlaced)
# 80x25 lines text
# 2 charsets with 256 characters each
# a char can be from 2 to 32 ppixels high. (always 8 horizontally)
# attributes per character
# color cells in bitmap mode
# blitter
# hardware scrolling

"""
wait until ready, then write value to register:
LDA #value
LDX #reg#
JSR $CDCC

which is:
CDCC: 8E 00 D6	STX $D600

CDCF: 2C 00 D6	BIT $D600
CDD2: 10 FB	BPL $CDCF
CDD4: 8D 01 D6	STA $D601
CDD7: 60	RTS

wait until Bit 7 of $D600 is set
write to $D600
write to $D601

read value from register:
LDX# reg#
JSR $CDDA
# A=value
"""
# there are 37 registers.
# unused bits are always set
R_THEORETICAL_CHAR_COUNT_PER_LINE = 0 # minus one. "should" be divisible by 8. default 126.
R_VIEW_CHAR_COUNT_PER_LINE = 1 # default 80.
R_HORIZONTAL_POSITION = 2 # >= [A_VIEW_CHARS_PER_LINE] # default 102. smaller means the image moves to the right.
R_VH_PULSE_WIDTH = 3 # bits 7..4 vert (0 is 16); bits 3..0 (horiz) ; default $49
R_THEORETICAL_LINE_COUNT = 4 # minus one. Including v blank.
R_LINE_COUNT_FINE = 5 # bits 4..0 ; default: $19
R_VIEW_LINE_COUNT = 6 # < [A_LINE_COUNT] # default $19
R_VERTICAL_POSITION = 7 # biggers means further down. default: $20
R_INTERLACING_MODE = 8 # bits 1..0: 00 and 01: noninterlace, 01 interlacesync, 10 interlace; default:
R_LINES_PER_CHARACTER = 9 # bits 4..0: value-1
R_CURSOR_MODE = 10 # bits 6..5: 00=noblink, 01=off, 10=fastblink, 11=normalblink; bits 4..0: cursor start
R_CURSOR_END = 11 # line+1 for the end
R_VIDEO_RAM_H = 12
R_VIDEO_RAM_L = 13
R_CURSOR_POS_H = 14
R_CURSOR_POS_L = 15
R_LIGHTPEN_VERTICAL_POSITION = 16
R_LIGHTPEN_HORIZONTAL_POSITION = 17
R_BLOCK_ADDRESS_H = 18 # autoinc
R_BLOCK_ADDRESS_L = 19
R_ATTRIBUTE_RAM_H = 20
R_ATTRIBUTE_RAM_L = 21
R_CHAR_WIDTH = 22
R_CHAR_HEIGHT = 23
R_CONTROL_1 = 24
R_CONTROL_2 = 25
R_MAIN_COLORS = 26
R_ROWSTRIDE = 27 # or just the padding?
R_CONTROL_3 = 28
R_UNDERLINE_POSITION = 29 # bits 4..0 position of underline; other bits always SET.
R_BLOCK_COPIER = 30
R_BLOCK_VALUE = 31
R_BLOCK_SOURCE_RAM_H = 32
R_BLOCK_SOURCE_RAM_L = 33
R_VIEW_FIRST_CELL_CHARS = 34
R_VIEW_LAST_CELL_CHARS = 35
R_DRAM_REFRESH = 36

# TODO actually decople this into its own thread?
class VDC(memory.Memory): # map at $D600
    def __init__(self):
        self.registers = [0x7E, 0x50, 0x66, 0x49, 0x27, 0x19, 0x19, 0x20, 0x20, 0xE7, 0xA0, 0xE7, 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0, 0x04, 0x00, 0x78, 0xE8, 0x20, 0x40, 0xF0, 0x00, 0x2F, 0xE7, 0, 0, 0, 0, 0x7D, 0x40, 0xF5]
        self.register_selector = 0
        self.status = 0x80 # FIXME VBlank, VDC version.
        self.B_idle = True
        self.B_VBlank = True
        self.pages = []
        for i in range(64):
            self.pages.append(ram.RAM())
    def read_register(self, address, size = 1):
        assert size == 1, "VDC.read_register: size==1"
        if(address < 37):
            # FIXME
            return(self.registers[address])
        else:
            print("VDC.read_register: tried to read out-of-bounds address $%X" % address)
            return(0xFF)
    def write_register(self, address, value, size):
        assert isinstance(value, int), "VDC.write_memory: value is an integer"
        assert size == 1, "VDC.write_register: value size is 1"
        if(address < 37):
            self.registers[address] = value
        else:
            print("VDC.write_register: tried to read out-of-bounds address $%X" % address)
        # FIXME
    def read_memory(self, address, size = 1):
        assert size == 1, "VDC.read_memory: size==1"
        if address == 0:
            return(0x80 if self.B_idle else 0) | (0x20 if self.B_VBlank else 0) # FIXME VDC version, light pen.
        elif address == 1:
            return(self.read_register(self.register_selector, size))
        else:
            print("VDC.read_memory: tried to read out-of-bounds address $%X" % address)
        return(0xFF)
    def write_memory(self, address, value, size = 1):
        assert isinstance(value, int), "VDC.write_memory: value is an integer"
        assert size == 1, "VDC.write_memory: value size==1"
        if address == 0:
            self.register_selector = value # FIXME
        elif address == 1:
            self.write_register(self.register_selector, value, size)
        else:
            print("VDC.write_memory: tried to read out-of-bounds address $%X" % address)
