Skip to content
Browse files

added true C64 emulator

  • Loading branch information...
irmen committed Sep 21, 2019
1 parent 7af0356 commit 66588e02752681999904d04c950f626cdb2e00bb
Showing with 334 additions and 104 deletions.
  1. +19 −9
  2. BIN demo_screenshot4.png
  3. +29 −11 pyc64/
  4. +95 −81 pyc64/
  5. +183 −0 pyc64/
  6. +2 −0 pyc64/
  7. +1 −1 requirements.txt
  8. +1 −1
  9. +1 −1
  10. +3 −0
@@ -5,7 +5,7 @@
... Or is it?

Well, it emulates the character mode *with sprites* quite well and thus you can display pretty PETSCII pictures if you want.
It's generally not possible to run actual C-64 software, but that's not the goal
It's generally not possible to run actual C-64 software, but that's not the real goal
of this project. (Look at actual c-64 emulators such as Vice or CCS64 if you want that)

@@ -19,15 +19,22 @@ you can only do the same things with it as what is supported
for the BASIC mode. So no fancy screen scrolling or rasterbars...

## can load and use real C64 ROMs
## Can run the real BASIC and KERNAL roms! True C64 emulation!

If basic/kernal/chargen ROM files are supplied in the roms folder,
the simulator will load these and make them part of the memory of the machine as ROM areas.
You can actually call machine code routines from the roms!
Such as the well-known reset routine, SYS 64738 .
The simulator will use this routine as well when the machine is reset!
(just a few slight kernel patches are made to make it compatible and to avoid ending
up in the actual ROM BASIC program loop - that doesn't work for now)
If you run the ``startreal64`` program, it will load the ``realemulator`` module
instead of the simulated one, and will *actually run the C64 BASIC ROM*.

You can type anything in BASIC that you could type on a real C64 too!

Unfortunately, the I/O is not emulated so it is not possible to load or save
your programs in this mode.

On my machine the current code runs at around 0.6 Mhz in regular Python
(around half the speed of a real c64),
and between 2 and 3 Mhz in Pypy (there is a lot more speed than this obtainable
with pypy, but that makes the c64's basic mode more or less unusable)

## BASIC V2 and the VIC-registers (video chip)
@@ -106,9 +113,12 @@ are included on the 'virtual disk' in this project)

PETSCII image:

![Screenshot two](demo_screenshot2.png)

Python mode:

![Screenshot two](demo_screenshot3.png)

Real emulation of a C64:

BIN +4.71 KB demo_screenshot4.png
Binary file not shown.
@@ -14,6 +14,7 @@
import tkinter
import pkgutil
import threading
import time
import queue
import time
from collections import deque
@@ -127,8 +128,9 @@ def __init__(self, screen, title, roms_directory):
self.border4 = self.canvas.create_rectangle(*b4, outline="", fill="#000")
self.bind("<KeyPress>", self.keypress)
# self.bind("<KeyPress>", self.keypress)
self.bind("<KeyRelease>", self.keyrelease)
self.bind("<Key>", self.keypress)

def start(self):
@@ -234,6 +236,7 @@ def repaint(self):
if configure:
# reconfigure all changed properties in one go
self.canvas.itemconfigure(self.spritebitmaps[snum], **configure)

def smoothscroll(self, xs, ys):
@@ -319,17 +322,20 @@ class C64EmulatorWindow(EmulatorWindowBase):
"use 'gopy' to enter Python mode\n\n\n\n" \
"(install the py64 library to be able to execute 6502 machine code)"

def __init__(self, screen, title, roms_directory):
def __init__(self, screen, title, roms_directory, run_real_roms):
super().__init__(screen, title, roms_directory)
self.screen.memory[0x00fb] = EmulatorWindowBase.update_rate
self.hertztick = threading.Event()
self.interpret_thread = None
self.interpreter = None
self.real_cpu_running = None
self.run_real_roms = run_real_roms

def start(self):
if not self.run_real_roms:

def _cyclic_herztick(self):
@@ -618,11 +624,19 @@ def _border_positions(self):
def reset_machine(self):
self.screen.memory[0x00fb] = EmulatorWindowBase.update_rate
if not self.run_real_roms:
if self.screen.using_roms:
print("using actual ROM reset routine (sys 64738)")
do_sys(self.screen, 64738, self.interpret_thread._microsleep, use_rom_routines=True)
reset = self.screen.memory.getword(0xfffc)
print("using actual ROM reset routine at", reset)
if self.run_real_roms:
if self.real_cpu_running is None:
threading.Thread(target=self.run_rom_code, args=(reset,), daemon=True).start()
do_sys(self.screen, reset, self.interpret_thread._microsleep, use_rom_routines=True)

class InterpretThread(threading.Thread):
@@ -748,13 +762,17 @@ def runstop(self):

def start():
def start(run_real_roms):
rom_directory = "roms"
screen = ScreenAndMemory(columns=C64EmulatorWindow.columns,
emu = C64EmulatorWindow(screen, "Commodore-64 simulator in pure Python!", rom_directory)
if run_real_roms:
emu = RealC64EmulatorWindow(screen, "Commodore-64 emulator in pure Python! - running actual roms", rom_directory)
emu = C64EmulatorWindow(screen, "Commodore-64 simulator in pure Python!", rom_directory, False)

0 comments on commit 66588e0

Please sign in to comment.
You can’t perform that action at this time.