Skip to content

Instantly share code, notes, and snippets.

@salexkidd
Created November 26, 2014 14:07
Show Gist options
  • Save salexkidd/e65a1f27d5a75545e206 to your computer and use it in GitHub Desktop.
Save salexkidd/e65a1f27d5a75545e206 to your computer and use it in GitHub Desktop.
My 6502 Controller and monitor
# -*- coding:utf-8 -*-
"""
6502 cpu driver for python
"""
from Adafruit.I2C.Adafruit_I2C import Adafruit_I2C
from Adafruit.MCP230xx.Adafruit_MCP230xx import Adafruit_MCP230XX as MCP230XX
from logging import (getLogger, StreamHandler, DEBUG)
import time
import os
MCP23017_PORT_NUM = 16
MAX_ADDRESSING = 64 * 1024
OTHER_BUS_I2C_ADDRESS = 0x20
ADDR_BUS_I2C_ADDRESS = 0x21
DATA_BUS_I2C_ADDRESS = 0x22
#input only pin number
SYNC_PIN_NUM = 0
RWB_PIN_NUM = 1
#output only pin number(PHI2 is clock pin)
PHI2_PIN_NUM = 8
RESET_PIN_NUM = 9
logger = getLogger(__name__)
handler = StreamHandler()
handler.setLevel(DEBUG)
logger.setLevel(DEBUG)
logger.addHandler(handler)
OPCODE_OF_6502 = {
0x00: ['BRK (Implied)', 1, 7],
0x01: ['ORA ((Indirect, X))', 2, 6],
0x05: ['ORA (Zeropage)', 2, 3],
0x06: ['ASL (Zeropage)', 2, 5],
0x08: ['PHP (Implied)', 1, 3],
0x09: ['ORA (Immediate)', 2, 2],
0x0A: ['ASL (Accumulator)', 1, 2],
0x0D: ['ORA (Absolute)', 3, 4],
0x0E: ['ASL (Absolute)', 3, 6],
0x10: ['BPL (Relative)', 2, 2],
0x11: ['ORA ((Indirect), Y)', 2, 5],
0x15: ['ORA (Zeropage, X)', 2, 4],
0x16: ['ASL (Zeropage, X)', 2, 6],
0x18: ['CLC (Implied)', 1, 2],
0x19: ['ORA (Absolute, Y)', 3, 4],
0x1D: ['ORA (Absolute, X)', 3, 4],
0x1E: ['ASL (Absolute, X)', 3, 7],
0x20: ['JSR (Absolute)', 3, 6],
0x21: ['AND ((Indirect, X))', 2, 6],
0x24: ['BIT (Zeropage)', 2, 3],
0x25: ['AND (Zeropage)', 2, 3],
0x26: ['ROL (Zeropage)', 2, 5],
0x28: ['PLP (Implied)', 1, 4],
0x29: ['AND (Immediate)', 2, 2],
0x2A: ['ROL (Accumulator)', 1, 2],
0x2C: ['BIT (Absolute)', 3, 4],
0x2D: ['AND (Absolute)', 3, 4],
0x2E: ['ROL (Absolute)', 3, 6],
0x30: ['BMI (Relative)', 2, 2],
0x31: ['AND ((Indirect), Y)', 2, 5],
0x35: ['AND (Zeropage, X)', 2, 4],
0x36: ['ROL (Zeropage, X)', 2, 6],
0x38: ['SEC (Implied)', 1, 2],
0x39: ['AND (Absolute, Y)', 3, 4],
0x3D: ['AND (Absolute, X)', 3, 4],
0x3E: ['ROL (Absolute, X)', 3, 7],
0x40: ['RTI (Implied)', 1, 6],
0x41: ['EOR ((Indirect, X))', 2, 6],
0x45: ['EOR (Zeropage)', 2, 3],
0x46: ['LSR (Zeropage)', 2, 5],
0x48: ['PHA (Implied)', 1, 3],
0x49: ['EOR (Immediate)', 2, 2],
0x4A: ['LSR (Accumulator)', 1, 2],
0x4C: ['JMP (Absolute)', 3, 3],
0x4D: ['EOR (Absolute)', 3, 4],
0x4E: ['LSR (Absolute)', 3, 6],
0x50: ['BVC (Relative)', 2, 2],
0x51: ['EOR ((Indirect), Y)', 2, 5],
0x55: ['EOR (Zeropage, X)', 2, 4],
0x56: ['LSR (Zeropage, X)', 2, 6],
0x58: ['CLI (Implied)', 1, 2],
0x59: ['EOR (Absolute, Y)', 3, 4],
0x5D: ['EOR (Absolute, X)', 3, 4],
0x5E: ['LSR (Absolute, X)', 3, 7],
0x60: ['RTS (Implied)', 1, 6],
0x61: ['ADC ((Indirect, X))', 2, 6],
0x65: ['ADC (Zeropage)', 2, 3],
0x66: ['ROR (Zeropage)', 2, 5],
0x68: ['PLA (Implied)', 1, 4],
0x69: ['ADC (Immediate)', 2, 2],
0x6A: ['ROR (Accumulator)', 1, 2],
0x6C: ['JMP ((Indirect Address))', 3, 5],
0x6D: ['ADC (Absolute)', 3, 4],
0x6E: ['ROR (Absolute)', 3, 6],
0x70: ['BVS (Relative)', 2, 2],
0x71: ['ADC ((Indirect), Y)', 2, 5],
0x75: ['ADC (Zeropage, X)', 2, 4],
0x76: ['ROR (Zeropage, X)', 2, 6],
0x78: ['SEI (Implied)', 1, 2],
0x79: ['ADC (Absolute, Y)', 3, 4],
0x7D: ['ADC (Absolute, X)', 3, 4],
0x7E: ['ROR (Absolute, X)', 3, 7],
0x81: ['STA ((Indirect, X))', 2, 6],
0x84: ['STY (Zeropage)', 2, 3],
0x85: ['STA (Zeropage)', 2, 3],
0x86: ['STX (Zeropage)', 2, 3],
0x88: ['DEY (Implied)', 1, 2],
0x8A: ['TXA (Implied)', 1, 2],
0x8C: ['STY (Absolute)', 3, 4],
0x8D: ['STA (Absolute)', 3, 4],
0x8E: ['STX (Absolute)', 3, 4],
0x90: ['BCC (Relative)', 2, 2],
0x91: ['STA ((Indirect), Y)', 2, 6],
0x94: ['STY (Zeropage, X)', 2, 4],
0x95: ['STA (Zeropage, X)', 2, 4],
0x96: ['STX (Zeropage, Y)', 2, 4],
0x98: ['TYA (Implied)', 1, 2],
0x99: ['STA (Absolute, Y)', 3, 5],
0x9A: ['TXS (Implied)', 1, 2],
0x9D: ['STA (Absolute, X)', 3, 5],
0xA0: ['LDY (Immediate)', 2, 2],
0xA1: ['LDA ((Indirect, X))', 2, 6],
0xA2: ['LDX (Immediate)', 2, 2],
0xA4: ['LDY (Zeropage)', 2, 3],
0xA5: ['LDA (Zeropage)', 2, 3],
0xA6: ['LDX (Zeropage)', 2, 3],
0xA8: ['TAY (Implied)', 1, 2],
0xA9: ['LDA (Immediate)', 2, 2],
0xAA: ['TAX (Implied)', 1, 2],
0xAC: ['LDY (Absolute)', 3, 4],
0xAD: ['LDA (Absolute)', 3, 4],
0xAE: ['LDX (Absolute)', 3, 4],
0xB0: ['BCS (Relative)', 2, 2],
0xB1: ['LDA ((Indirect), Y)', 2, 5],
0xB4: ['LDY (Zeropage, X)', 2, 4],
0xB5: ['LDA (Zeropage, X)', 2, 4],
0xB6: ['LDX (Zeropage, Y)', 2, 4],
0xB8: ['CLV (Implied)', 1, 2],
0xB9: ['LDA (Absolute, Y)', 3, 4],
0xBA: ['TSX (Implied)', 1, 2],
0xBC: ['LDY (Absolute, X)', 3, 4],
0xBD: ['LDA (Absolute, X)', 3, 4],
0xBE: ['LDX (Absolute, Y)', 3, 4],
0xC0: ['CPY (Immediate)', 2, 2],
0xC1: ['CMP ((Indirect, X))', 2, 6],
0xC4: ['CPY (Zeropage)', 2, 3],
0xC5: ['CMP (Zeropage)', 2, 3],
0xC6: ['DEC (Zeropage)', 2, 5],
0xC8: ['INY (Implied)', 1, 2],
0xC9: ['CMP (Immediate)', 2, 2],
0xCA: ['DEX (Implied)', 1, 2],
0xCC: ['CPY (Absolute)', 3, 4],
0xCD: ['CMP (Absolute)', 3, 4],
0xCE: ['DEC (Absolute)', 3, 6],
0xD0: ['BNE (Relative)', 2, 2],
0xD1: ['CMP ((Indirect), Y)', 2, 5],
0xD5: ['CMP (Zeropage, X)', 2, 4],
0xD6: ['DEC (Zeropage, X)', 2, 6],
0xD8: ['CLD (Implied)', 1, 2],
0xD9: ['CMP (Absolute, Y)', 3, 4],
0xDD: ['CMP (Absolute, X)', 3, 4],
0xDE: ['DEC (Absolute, X)', 3, 7],
0xE0: ['CPX (Immediate)', 2, 2],
0xE1: ['SBC ((Indirect, X))', 2, 6],
0xE4: ['CPX (Zeropage)', 2, 3],
0xE5: ['SBC (Zeropage)', 2, 3],
0xE6: ['INC (Zeropage)', 2, 5],
0xE8: ['INX (Implied)', 1, 2],
0xE9: ['SBC (Immediate)', 2, 2],
0xEA: ['NOP (Implied)', 1, 2],
0xEC: ['CPX (Absolute)', 3, 4],
0xED: ['SBC (Absolute)', 3, 4],
0xEE: ['INC (Absolute)', 3, 6],
0xF0: ['BEQ (Relative)', 2, 2],
0xF1: ['SBC ((Indirect), Y)', 2, 5],
0xF5: ['SBC (Zeropage, X)', 2, 4],
0xF6: ['INC (Zeropage, X)', 2, 6],
0xF8: ['SED (Implied)', 1, 2],
0xF9: ['SBC (Absolute, Y)', 3, 4],
0xFD: ['SBC (Absolute, X)', 3, 4],
0xFE: ['INC (Absolute, X)', 3, 7]
}
class Memory(object):
""" It is Memory """
def __init__(self, size=MAX_ADDRESSING, rom_path=""):
#clear mem
self._space = [0x0 for i in xrange(MAX_ADDRESSING)]
#read rom and put to memory
with open(rom_path, "rb") as fh:
rom_size = os.path.getsize(rom_path)
for i, l in enumerate(fh.read()):
self._space[i + (MAX_ADDRESSING - rom_size)] = ord(l)
logger.debug("Memory Initialized")
class CPU6502Controller(object):
""" It is CPU6502 controller class """
def __init__(self, memory, addr_bus, data_bus, other_bus):
self._addr_bus = addr_bus
self._data_bus = data_bus
self._other_bus = other_bus
self._memory = memory
self._address = 0x0
self._data = 0x0
logger.debug("CPU Controller Initialized")
def reset(self):
logger.debug("reset")
self._other_bus.output(RESET_PIN_NUM, 0)
self._other_bus.output(RESET_PIN_NUM, 1)
def _send_clock(self):
""" send clock """
self._other_bus.output(PHI2_PIN_NUM, 0)
time.sleep(0.01)
self._other_bus.output(PHI2_PIN_NUM, 1)
time.sleep(0.01)
def _get_address(self):
self._address = sum(
[self._addr_bus.input(i) for i in xrange(16)]
)
def _cpu_need_data(self):
for i in xrange(8):
self._data_bus.config(i, MCP230XX.OUTPUT)
for i, v in enumerate(bin(self._memory._space[self._address])[2:].zfill(8)):
self._data_bus.output(i, int(v))
logger.debug("DATA: 0x{:02x}".format(self._memory._space[self._address]))
def _cpu_give_data(self):
for i in xrange(16):
self._data_bus.config(i, MCP230XX.INPUT)
#TODO: read data pin and store data!
bits = ""
for b in [self._data_bus.input(i) for i in xrange(8)]:
bits += "1" if b else "0"
data = int(bits, 2)
self._memory._space[self._address] = data
logger.debug("WROTE IO: [0x{:02x}]".format(data))
def _get_rwb_flg(self):
return self._other_bus.input(RWB_PIN_NUM) >> RWB_PIN_NUM
def _get_sync_flg(self):
return self._other_bus.input(SYNC_PIN_NUM) >> SYNC_PIN_NUM
def run(self):
""" run cpu! """
self.reset()
while True:
logger.debug("--------------------------")
self._send_clock()
self._get_address()
rwb_flg = self._get_rwb_flg()
sync_flg = self._get_sync_flg()
if rwb_flg:
self._cpu_need_data()
else:
self._cpu_give_data()
logger.debug("ADDRESS: 0x{:04x}".format(self._address))
if sync_flg:
op_code = self._memory._space[self._address]
try:
op = OPCODE_OF_6502[self._memory._space[self._address]]
logger.debug("OPCODE: {}(0x{:02x}) (Byte:{} Cycle:{})".format(
op[0], op_code, op[1], op[2]))
except KeyError as e:
logger.debug(
"Don't know opcode: 0x{:02x}".format(op_code))
logger.debug("(SYNC:{}) (RWB:{}) > ".format(sync_flg, rwb_flg))
# raw_input(
# "(SYNC:{}) (RWB:{}) > ".format(sync_flg, rwb_flg)
# )
def setup_i2c():
other_bus = MCP230XX(OTHER_BUS_I2C_ADDRESS, MCP23017_PORT_NUM)
#SYNC and RWB
for i in range(0, MCP23017_PORT_NUM / 2):
other_bus.config(i, MCP230XX.INPUT)
other_bus.pullup(i, 1)
#PHI2(clock) and RESET
for i in range(MCP23017_PORT_NUM / 2, MCP23017_PORT_NUM):
other_bus.config(i, MCP230XX.OUTPUT)
#ADDRESS BUS Setup
addr_bus = MCP230XX(ADDR_BUS_I2C_ADDRESS, MCP23017_PORT_NUM)
for i in xrange(16):
addr_bus.config(i, MCP230XX.INPUT)
addr_bus.pullup(i, 1)
#DATA BUS Setup
data_bus = MCP230XX(DATA_BUS_I2C_ADDRESS, MCP23017_PORT_NUM)
for i in xrange(8):
data_bus.config(i, MCP230XX.OUTPUT)
# It's Experiment!
for i in xrange(8, 16):
data_bus.config(i, MCP230XX.INPUT)
data_bus.pullup(i, 1)
return other_bus, addr_bus, data_bus
def power_on():
""" power on """
other_bus, addr_bus, data_bus = setup_i2c()
memory = Memory(rom_path="../roms/A2ROM.BIN")
cpu_controller = CPU6502Controller(memory, addr_bus, data_bus, other_bus)
cpu_controller.run()
def main():
power_on()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment