Sophie

Sophie

distrib > Mandriva > 2010.2 > i586 > media > contrib-backports > by-pkgid > df29c83ca401d91ec9c00bfcf7fea4ea > files > 125

shedskin-0.8-2mdv2010.2.i586.rpm

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

import sys
import os
import cpu
import vic_ii
import sid
import cia
import time
import tape
#import gmonitor
import memory
#import gdisplay
from symbols import *

#sys.path = [os.path.realpath("pygtk_s")] + sys.path

SPRITE_COUNT = 8

#import psyco
#psyco.full()


class CPUPort(memory.Memory): # $0..$1
    def __init__(self, MMU):
        memory.Memory.__init__(self)
        self.B_can_write = True # in the instance because of ShedSkin
        self.B_active = True
        self.MMU = MMU
        self.data_direction = 0x2F # RW for port below
        self.port = 0
        """
        port:
            bits 0..2: configuration for ROM memory:
                bx00: RAM visible in all areas.
                bx01: RAM visible at $A000..BFFF, $E000..$FFFF.
                bx10: RAM visible at $A000..BFFF, KERNAL ROM AT $E000..$FFFF.
                bx11: BASIC ROM visible at $A000..BFFF, KERNAL ROM AT $E000..$FFFF.
                b0xx: Character ROM visible at $D000..$DFFF except for b000.
                b1xx: IO area visible at $D000..$DFFF except for b100.
            bit 3: datasette output signal level
            bit 4: datasette button status (inverse).
            bit 5: motor control (inverse).
        """
        self.set_port(0x37)

    def read_memory(self, address, size = 1):
        assert size == 1, "CPUPort.read_memory size=1"
        if address == 0:
            return self.data_direction
        elif address == 1:
            return self.port
        else:
            assert False, "CPUPort.read_memory address is known"
            return 0

    def write_memory(self, address, value, size):
        assert isinstance(value, int), "CPUPort.write_memory value written is an integer"
        if address == 0:
            self.data_direction = value # FIXME protect?
        elif address == 1:
            self.set_port(value)
        else:
            assert False, "CPUPort.write_memory address is known"

    def set_port(self, value):
        if self.port == value:
            return
#        if value & 32:
#            print("cassette motor off")
#        else:
#            print("cassette motor on")
        self.port = value
        B_all_visible = (value & 3) == 0 # all RAM
        low = (value & 3)
        B_A000_RAM = low == 1 or low == 0x10 or B_all_visible
        B_E000_RAM = low == 1 or B_all_visible
        B_D000_Character = (value & 4) == 0 and not B_all_visible
        B_D000_IO = (value & 4) != 0 and not B_all_visible
        self.MMU.set_overlay_active("basic", not B_A000_RAM)
        self.MMU.set_overlay_active("chargen", B_D000_Character)
        self.MMU.set_overlay_active("vic", B_D000_IO)
        self.MMU.set_overlay_active("sid", B_D000_IO)
        self.MMU.set_overlay_active("cia1", B_D000_IO)
        self.MMU.set_overlay_active("cia2", B_D000_IO)
        self.MMU.set_overlay_active("kernal", not B_E000_RAM)
        #if B_E000_RAM:
        #    sys.stderr.write("warning: KERNAL disabled!!!\n")
            #time.sleep(5.0)

class C64(object):
    def __init__(self):
        self.interrupt_clock = 0
        self.VIC_clock = 0
        self.CPU = cpu.CPU()
        MMU = self.CPU.MMU
        address = 0
        # power-up pattern:
        for i in range(512):
            for b in range(64):
                MMU.write_memory(address, 0, 1)
                address += 1
            for b in range(64):
                MMU.write_memory(address, 0xFF, 1)
                address += 1
            
        self.ROMs = [
            ("basic",   (0xA000, 0xC000)),
            ("chargen", (0xD000, 0xE000)),
            #("dos1541", (0xD000, 0xE000)),
            ("kernal",  (0xE000, 0x10000)),
        ]

        char_ROM = None
        for ROM, range_1 in self.ROMs:
            value = open("ROM/bin/C64/" + ROM, "rb").read()
            size = range_1[1] - range_1[0]
            #print(ROM)
            assert size == len(value), "C64 ROM is not truncated"
            # TODO how to do this in a nicer way?
            if ROM == "kernal":
                hardware_vectors = value[-6:]
                value = value[:-6]
                for i in range(6):
                    self.CPU.MMU.write_memory(0xFFFA + i, ord(hardware_vectors[i]), 1)
                # patch ROM so BRK instructions are at strategic places.
                xvalue = [c for c in value]
                for address in tape.get_hooks():
                    assert address >= 0xE000, "hooks are actually within the KERNAL"
                    xvalue[address - 0xE000] = chr(0) # BRK
                xvalue[0xE4E2 - 0xE000] = chr(0x60) # get rid of tape delay
                value = b"".join(xvalue)
            ROM_obj = self.CPU.MMU.map_ROM(ROM, range_1[0], value, ROM != "chargen")
            if ROM == "chargen":
                char_ROM = ROM_obj


        cia1 = cia.CIA1()
        self.CIA1 = cia1
        cia2 = cia.CIA2()
        self.CIA2 = cia2
        vic = vic_ii.VIC_II(self, self.CPU.MMU, cia2, char_ROM)
        self.VIC = vic
        self.CPU.MMU.map_IO("cia2", (0xDD00, 0xDE00), cia2)
        self.CPU.MMU.map_IO("vic", (0xD000, 0xD400), vic)
        self.CPU.MMU.map_IO("sid", (0xD400, 0xD800), sid.SID())
        self.CPU.MMU.map_IO("cia1", (0xDC00, 0xDD00), cia1)
        self.CPU.MMU.map_IO("cpu", (0x0000, 0x0002), CPUPort(self.CPU.MMU))
        vic.repaint() # ShedSkin
        vic.unprepare() # memory is not initialized yet, so unprepare...
        MMU = self.CPU.MMU
        #MMU.write_memory(0xFFFA, b"\x43\xFE\xE2\xFC\x48\xFF") # FIXME endianness.
        #self.CPU.BRK(0)
        # done automatically on "BRK"?
        self.CPU.write_register(S_PC, (MMU.read_memory(0xFFFC, 2)))
        self.fire_timer() # ShedSkin
        #self.controls = gmonitor.Controls(self)
        #self.controls = {}

    def set_tape_image_name(self, name, format):
        tape.set_image_name(name, format)

    def run(self):
        while True: # TODO terminate?
            self.iterate()

    def fire_timer(self):
        for n in range(2000):
            self.iterate()
            self.interrupt_clock += 1
            self.VIC_clock += 1
            if self.VIC_clock >= 10: # 6:
                self.VIC_clock = 0
                self.VIC.increase_raster_position()
        if self.interrupt_clock >= 50: # FIXME remove
            self.interrupt_clock = 0
            self.cause_interrupt()
        self.VIC.repaint()
        if self.VIC_clock >= 1: # 6:
            self.VIC_clock = 0
            self.VIC.increase_raster_position()
        return
        self.iterate()
        self.interrupt_clock += 1
        if self.interrupt_clock >= 50: # FIXME remove
            self.interrupt_clock = 0
            self.cause_interrupt()
        return

    def iterate(self):
        self.CPU.step()
        return True

    def cause_interrupt(self):
        if "I" in self.CPU.flags: # interrupt DISABLE
            #print("not supposed to cause interrupts right now...")
            return True
        #print("at 0x0283: %r" % self.CPU.MMU.read_memory(0x0283, 2))
        #print("at 0x37: %r" % self.CPU.MMU.read_memory(0x37, 2))
        #print("at 0x2B: %r" % self.CPU.MMU.read_memory(0x2B, 2))
        #if not self.CIA1.B_interrupt_pending:
        if not self.CPU.B_in_interrupt:
            #self.CIA1.B_interrupt_pending = True
            self.CPU.cause_interrupt(False)
        return True


"""
$D000-$DFFF
53248-57343    

I/O Area (memory mapped chip registers), Character ROM or RAM area (4096 bytes); depends on the value of bits #0-#2 of the processor port at memory address $0001:

    *

      %x00: RAM area.
    *

      %0xx: Character ROM. (Except for the value %000, see above.)
    *

      %1xx: I/O Area. (Except for the value %100, see above.)
"""

if __name__ == "__main__":
    c64 = C64()
    c64.CIA1.handle_key_press("X")
    c64.CIA1.handle_key_release("X")
    c64.CIA1.read_memory(0, 1)
    # clear_Z, set_Z, clear_N, set_N, set_V
    # timeout_remove
    for i in range(800000):
        c64.iterate()
    #c64.CPU_clock = timer.timeout_add(5, c64)
    #c64.cause_interrupt() # ShedSkin
    #{
    import gtk
    gtk.main()
    #}
    #c64.run()
    c64.run()
    c64.set_tape_image_name("stuff", "T64") # ShedSkin