1
0
mirror of https://github.com/mnaberez/py65.git synced 2024-06-08 19:29:27 +00:00
This commit is contained in:
Mike Naberezny 2012-11-19 12:44:30 -08:00
parent e8565a6e81
commit 8f284a8e58
25 changed files with 1815 additions and 1181 deletions

View File

@ -6,6 +6,8 @@
- Fixed assembling 65C02 opcodes whose mnemonics have a digit
such as "RMB3".
- Reformatted source code to comply with PEP8.
0.13 (2012-11-15)
- Fixed a bug where negative numbers could be entered

View File

@ -3,15 +3,18 @@
# test documentation build configuration file, created by
# sphinx-quickstart on Sun Nov 30 14:39:06 2008.
#
# This file is execfile()d with the current directory set to its containing dir.
# This file is execfile()d with the current dir set to its containing
# directory.
#
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed automatically).
# that aren't pickleable (module imports are okay, they're removed
# automatically).
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
import os
import sys
# If your extensions are in another directory, add it here. If the directory
# is relative to the documentation root, use os.path.abspath to make it
@ -21,8 +24,8 @@ import sys, os
# General configuration
# ---------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
@ -67,7 +70,8 @@ release = '0.14-dev'
# for source files.
exclude_trees = []
# The reST default role (used for this markup: `text`) to use for all documents.
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
@ -163,10 +167,11 @@ htmlhelp_basename = 'testdoc'
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, document class [howto/manual]).
# (source start file, target name, title, author, document class
# [howto/manual]).
latex_documents = [
('index', 'test.tex', ur'test Documentation',
ur'foo', 'manual'),
('index', 'test.tex', ur'test Documentation',
ur'foo', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of

View File

@ -2,44 +2,76 @@ import re
from py65.devices.mpu6502 import MPU
from py65.utils.addressing import AddressParser
class Assembler:
Statement = re.compile(r'^([A-z]{3}[0-7]?\s+'
r'\(?\s*)([^,\s\)]+)(\s*[,xXyY\s]*\)?'
r'[,xXyY\s]*)$')
Addressing8 = [
['zpi', re.compile(r'^\(\$00([0-9A-F]{2})\)$')], # "($0012)"
['zpx', re.compile(r'^\$00([0-9A-F]{2}),X$')], # "$0012,X"
['zpy', re.compile(r'^\$00([0-9A-F]{2}),Y$')], # "$0012,Y"
['zpg', re.compile(r'^\$00([0-9A-F]{2})$')], # "$0012"
['inx', re.compile(r'^\(\$00([0-9A-F]{2}),X\)$')], # "($0012,X)"
['iny', re.compile(r'^\(\$00([0-9A-F]{2})\),Y$')], # "($0012),Y"
['ind', re.compile(r'^\(\$([0-9A-F]{2})([0-9A-F]{2})\)$')], # "($1234)"
['abx', re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2}),X$')], # "$1234,X"
['aby', re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2}),Y$')], # "$1234,Y"
['abs', re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2})$')], # "$1234"
['rel', re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2})$')], # "$1234"
['imp', re.compile(r'^$')], # ""
['acc', re.compile(r'^$')], # ""
['acc', re.compile(r'^A$')], # "A"
['imm', re.compile(r'^#\$([0-9A-F]{2})$')] # "#$12"
['zpi', # "($0012)"
re.compile(r'^\(\$00([0-9A-F]{2})\)$')],
['zpx', # "$0012,X"
re.compile(r'^\$00([0-9A-F]{2}),X$')],
['zpy', # "$0012,Y"
re.compile(r'^\$00([0-9A-F]{2}),Y$')],
['zpg', # "$0012"
re.compile(r'^\$00([0-9A-F]{2})$')],
['inx', # "($0012,X)
re.compile(r'^\(\$00([0-9A-F]{2}),X\)$')],
['iny', # "($0012),Y"
re.compile(r'^\(\$00([0-9A-F]{2})\),Y$')],
['ind', # "($1234)"
re.compile(r'^\(\$([0-9A-F]{2})([0-9A-F]{2})\)$')],
['abx', # "$1234,X"
re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2}),X$')],
['aby', # "$1234,Y"
re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2}),Y$')],
['abs', # "$1234"
re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2})$')],
['rel', # "$1234"
re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2})$')],
['imp', # ""
re.compile(r'^$')],
['acc', # ""
re.compile(r'^$')],
['acc', # "A"
re.compile(r'^A$')],
['imm', # "#$12"
re.compile(r'^#\$([0-9A-F]{2})$')]
]
Addressing16 = [
['zpi', re.compile(r'^\(\$0000([0-9A-F]{4})\)$')], # "($00001234)"
['zpx', re.compile(r'^\$0000([0-9A-F]{4}),X$')], # "$00001234,X"
['zpy', re.compile(r'^\$0000([0-9A-F]{4}),Y$')], # "$00001234,Y"
['zpg', re.compile(r'^\$0000([0-9A-F]{4})$')], # "$00001234"
['inx', re.compile(r'^\(\$0000([0-9A-F]{4}),X\)$')], # "($00001234,X)"
['iny', re.compile(r'^\(\$0000([0-9A-F]{4})\),Y$')], # "($00001234),Y"
['ind', re.compile(r'^\(\$([0-9A-F]{4})([0-9A-F]{4})\)$')], # "($12345678)"
['abx', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4}),X$')], # "$12345678,X"
['aby', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4}),Y$')], # "$12345678,Y"
['abs', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4})$')], # "$12345678"
['rel', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4})$')], # "$12345678"
['imp', re.compile(r'^$')], # ""
['acc', re.compile(r'^$')], # ""
['acc', re.compile(r'^A$')], # "A"
['imm', re.compile(r'^#\$([0-9A-F]{4})$')] # "#$1234"
['zpi', # "($00001234)"
re.compile(r'^\(\$0000([0-9A-F]{4})\)$')],
['zpx', # "$00001234,X"
re.compile(r'^\$0000([0-9A-F]{4}),X$')],
['zpy', # "$00001234,Y"
re.compile(r'^\$0000([0-9A-F]{4}),Y$')],
['zpg', # "$00001234"
re.compile(r'^\$0000([0-9A-F]{4})$')],
['inx', # "($00001234,X)"
re.compile(r'^\(\$0000([0-9A-F]{4}),X\)$')],
['iny', # "($00001234),Y"
re.compile(r'^\(\$0000([0-9A-F]{4})\),Y$')],
['ind', # "($12345678)"
re.compile(r'^\(\$([0-9A-F]{4})([0-9A-F]{4})\)$')],
['abx', # "$12345678,X"
re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4}),X$')],
['aby', # "$12345678,Y"
re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4}),Y$')],
['abs', # "$12345678"
re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4})$')],
['rel', # "$12345678"
re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4})$')],
['imp', # ""
re.compile(r'^$')],
['acc', # ""
re.compile(r'^$')],
['acc', # "A"
re.compile(r'^A$')],
['imm', # "#$1234"
re.compile(r'^#\$([0-9A-F]{4})$')]
]
Addressing = Addressing8
@ -77,7 +109,7 @@ class Assembler:
if match:
try:
bytes = [ self._mpu.disassemble.index((opcode, mode)) ]
bytes = [self._mpu.disassemble.index((opcode, mode))]
except ValueError:
continue
@ -88,13 +120,13 @@ class Assembler:
absolute = int(''.join(operands), 16)
relative = (absolute - pc) - 2
relative = relative & self.byteMask
operands = [ (self.byteFmt % relative) ]
operands = [(self.byteFmt % relative)]
elif len(operands) == 2:
# swap bytes
operands = (operands[1], operands[0])
operands = [ int(hex, 16) for hex in operands ]
operands = [int(hex, 16) for hex in operands]
bytes.extend(operands)
return bytes

View File

@ -1,34 +1,35 @@
from py65.utils.conversions import convert_to_bin, convert_to_bcd, itoa
from py65.utils.devices import make_instruction_decorator
class MPU:
# vectors
ResetTo = 0xfffc
IrqTo = 0xfffe
NMITo = 0xfffa
IrqTo = 0xfffe
NMITo = 0xfffa
# processor flags
NEGATIVE = 128
OVERFLOW = 64
UNUSED = 32
BREAK = 16 # there is no BREAK flag, but this position indicates BREAK
DECIMAL = 8
NEGATIVE = 128
OVERFLOW = 64
UNUSED = 32
BREAK = 16 # there is no actual BREAK flag but this indicates BREAK
DECIMAL = 8
INTERRUPT = 4
ZERO = 2
CARRY = 1
ZERO = 2
CARRY = 1
BYTE_WIDTH = 8
BYTE_WIDTH = 8
BYTE_FORMAT = "%02x"
ADDR_WIDTH = 16
ADDR_WIDTH = 16
ADDR_FORMAT = "%04x"
def __init__(self, memory=None, pc=0x0000):
# config
self.name = '6502'
self.byteMask = ((1<<self.BYTE_WIDTH)-1)
self.addrMask = ((1<<self.ADDR_WIDTH)-1)
self.addrHighMask = (self.byteMask<<self.BYTE_WIDTH)
self.spBase = 1<<self.BYTE_WIDTH
self.byteMask = ((1 << self.BYTE_WIDTH) - 1)
self.addrMask = ((1 << self.ADDR_WIDTH) - 1)
self.addrHighMask = (self.byteMask << self.BYTE_WIDTH)
self.spBase = 1 << self.BYTE_WIDTH
# vm status
self.excycles = 0
@ -44,16 +45,15 @@ class MPU:
self.reset()
def reprformat(self):
return ("%s PC AC XR YR SP NV-BDIZC\n" + \
"%s: %04x %02x %02x %02x %02x %s"
)
return ("%s PC AC XR YR SP NV-BDIZC\n"
"%s: %04x %02x %02x %02x %02x %s")
def __repr__(self):
flags = itoa(self.p, 2).rjust(self.BYTE_WIDTH, '0')
flags = itoa(self.p, 2).rjust(self.BYTE_WIDTH, '0')
indent = ' ' * (len(self.name) + 2)
return self.reprformat() % (indent, self.name,
self.pc, self.a, self.x, self.y, self.sp, flags)
return self.reprformat() % (indent, self.name, self.pc, self.a,
self.x, self.y, self.sp, flags)
def step(self):
instructCode = self.memory[self.pc]
@ -61,7 +61,7 @@ class MPU:
self.excycles = 0
self.addcycles = self.extracycles[instructCode]
self.instruct[instructCode](self)
self.processorCycles += self.cycletime[instructCode]+self.excycles
self.processorCycles += self.cycletime[instructCode] + self.excycles
return self
def reset(self):
@ -103,20 +103,20 @@ class MPU:
return self.byteMask & (self.y + self.ByteAt(self.pc))
def IndirectXAddr(self):
return self.WrapAt( self.byteMask & (self.ByteAt(self.pc) + self.x))
return self.WrapAt(self.byteMask & (self.ByteAt(self.pc) + self.x))
def IndirectYAddr(self):
if self.addcycles:
a1 = self.WrapAt(self.ByteAt(self.pc))
a2 = (a1+self.y) & self.addrMask
a2 = (a1 + self.y) & self.addrMask
if (a1 & self.addrHighMask) != (a2 & self.addrHighMask):
self.excycles += 1
return a2
else:
return (self.WrapAt(self.ByteAt(self.pc))+self.y)&self.addrMask
return (self.WrapAt(self.ByteAt(self.pc)) + self.y) & self.addrMask
def AbsoluteAddr(self):
return self.WordAt(self.pc)
return self.WordAt(self.pc)
def AbsoluteXAddr(self):
if self.addcycles:
@ -126,7 +126,7 @@ class MPU:
self.excycles += 1
return a2
else:
return (self.WordAt(self.pc)+self.x)&self.addrMask
return (self.WordAt(self.pc) + self.x) & self.addrMask
def AbsoluteYAddr(self):
if self.addcycles:
@ -136,7 +136,7 @@ class MPU:
self.excycles += 1
return a2
else:
return (self.WordAt(self.pc)+self.y)&self.addrMask
return (self.WordAt(self.pc) + self.y) & self.addrMask
def BranchRelAddr(self):
self.excycles += 1
@ -155,23 +155,23 @@ class MPU:
# stack
def stPush(self,z):
self.memory[self.sp+self.spBase] = z&self.byteMask
def stPush(self, z):
self.memory[self.sp + self.spBase] = z & self.byteMask
self.sp -= 1
self.sp &= self.byteMask
def stPop(self):
self.sp += 1
self.sp &= self.byteMask
return self.ByteAt(self.sp+self.spBase)
return self.ByteAt(self.sp + self.spBase)
def stPushWord(self, z):
self.stPush((z>>self.BYTE_WIDTH)&self.byteMask)
self.stPush(z&self.byteMask)
self.stPush((z >> self.BYTE_WIDTH) & self.byteMask)
self.stPush(z & self.byteMask)
def stPopWord(self):
z = self.stPop()
z += self.stPop()<<self.BYTE_WIDTH
z += self.stPop() << self.BYTE_WIDTH
return z
def FlagsNZ(self, value):
@ -217,34 +217,34 @@ class MPU:
addr = x()
tbyte = self.ByteAt(addr)
self.p &=~(self.CARRY+self.NEGATIVE+self.ZERO)
self.p |=tbyte&1
self.p &= ~(self.CARRY + self.NEGATIVE + self.ZERO)
self.p |= tbyte & 1
tbyte = tbyte >> 1
if tbyte:
pass # {}
pass
else:
self.p |= self.ZERO
if x is None:
self.a = tbyte
else:
self.memory[addr]=tbyte
self.memory[addr] = tbyte
def opBCL(self, x):
if self.p & x:
self.pc += 1
else:
self.BranchRelAddr()
if self.p & x:
self.pc += 1
else:
self.BranchRelAddr()
def opBST(self, x):
if self.p & x:
self.BranchRelAddr()
else:
self.pc += 1
if self.p & x:
self.BranchRelAddr()
else:
self.pc += 1
def opCLR(self, x):
self.p &=~x
self.p &= ~x
def opSET(self, x):
self.p |= x
@ -255,10 +255,10 @@ class MPU:
def opBIT(self, x):
tbyte = self.ByteAt(x())
self.p &=~(self.ZERO+self.NEGATIVE+self.OVERFLOW)
self.p &= ~(self.ZERO + self.NEGATIVE + self.OVERFLOW)
if (self.a & tbyte) == 0:
self.p |= self.ZERO
self.p |= tbyte&(self.NEGATIVE+self.OVERFLOW)
self.p |= tbyte & (self.NEGATIVE + self.OVERFLOW)
def opROL(self, x):
if x is None:
@ -281,7 +281,7 @@ class MPU:
self.FlagsNZ(tbyte)
if x is None:
self.a = tbyte
self.a = tbyte
else:
self.memory[addr] = tbyte
@ -314,14 +314,14 @@ class MPU:
# the final A contents will be decimally adjusted
nibble0 = (nibble0 + adjust0) & 0xf
nibble1 = (nibble1 + adjust1) & 0xf
self.p &= ~(self.CARRY+self.OVERFLOW+self.NEGATIVE+self.ZERO)
self.p &= ~(self.CARRY + self.OVERFLOW + self.NEGATIVE + self.ZERO)
if aluresult == 0:
self.p |= self.ZERO
else:
self.p |= aluresult & self.NEGATIVE
if decimalcarry == 1:
self.p |= self.CARRY
if ( ~(self.a ^ data) & (self.a ^ aluresult) ) & self.NEGATIVE:
if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE:
self.p |= self.OVERFLOW
self.a = (nibble1 << 4) + nibble0
else:
@ -330,13 +330,13 @@ class MPU:
else:
tmp = 0
result = data + self.a + tmp
self.p &= ~(self.CARRY+self.OVERFLOW+self.NEGATIVE+self.ZERO)
if ( ~(self.a ^ data) & (self.a ^ result) ) & self.NEGATIVE:
self.p &= ~(self.CARRY + self.OVERFLOW + self.NEGATIVE + self.ZERO)
if (~(self.a ^ data) & (self.a ^ result)) & self.NEGATIVE:
self.p |= self.OVERFLOW
data = result
if data > self.byteMask:
self.p |= self.CARRY
data &=self.byteMask
data &= self.byteMask
if data == 0:
self.p |= self.ZERO
else:
@ -347,19 +347,19 @@ class MPU:
if x is None:
tbyte = self.a
else:
addr=x()
addr = x()
tbyte = self.ByteAt(addr)
if self.p & self.CARRY:
if tbyte & 1:
pass # {}
pass
else:
self.p &=~ self.CARRY
tbyte=(tbyte>>1)|self.NEGATIVE
self.p &= ~self.CARRY
tbyte = (tbyte >> 1) | self.NEGATIVE
else:
if tbyte & 1:
self.p |= self.CARRY
tbyte=tbyte>>1
if tbyte & 1:
self.p |= self.CARRY
tbyte = tbyte >> 1
self.FlagsNZ(tbyte)
if x is None:
@ -378,7 +378,7 @@ class MPU:
def opCMPR(self, get_address, register_value):
tbyte = self.ByteAt(get_address())
self.p &= ~(self.CARRY+self.ZERO+self.NEGATIVE)
self.p &= ~(self.CARRY + self.ZERO + self.NEGATIVE)
if register_value == tbyte:
self.p |= self.CARRY + self.ZERO
elif register_value > tbyte:
@ -386,59 +386,61 @@ class MPU:
self.p |= (register_value - tbyte) & self.NEGATIVE
def opSBC(self, x):
data = self.ByteAt(x())
data = self.ByteAt(x())
if self.p & self.DECIMAL:
halfcarry = 1
decimalcarry = 0
adjust0 = 0
adjust1 = 0
if self.p & self.DECIMAL:
halfcarry = 1
decimalcarry = 0
adjust0 = 0
adjust1 = 0
nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY)
if nibble0 <= 0xf:
halfcarry = 0
adjust0 = 10
nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry
if nibble1 <= 0xf:
adjust1 = 10 << 4
nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY)
if nibble0 <= 0xf:
halfcarry = 0
adjust0 = 10
nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry
if nibble1 <= 0xf:
adjust1 = 10 << 4
# the ALU outputs are not decimally adjusted
aluresult = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
if aluresult > self.byteMask:
decimalcarry = 1
aluresult &= self.byteMask
# the ALU outputs are not decimally adjusted
aluresult = self.a + (~data & self.byteMask) + \
(self.p & self.CARRY)
# but the final result will be adjusted
nibble0 = (aluresult + adjust0) & 0xf
nibble1 = ((aluresult + adjust1) >> 4) & 0xf
if aluresult > self.byteMask:
decimalcarry = 1
aluresult &= self.byteMask
self.p &= ~(self.CARRY + self.ZERO + self.NEGATIVE + self.OVERFLOW)
if aluresult == 0:
self.p |= self.ZERO
else:
self.p |= aluresult & self.NEGATIVE
if decimalcarry == 1:
self.p |= self.CARRY
if ( (self.a ^ data) & (self.a ^ aluresult) ) & self.NEGATIVE:
self.p |= self.OVERFLOW
self.a = (nibble1 << 4) + nibble0
else:
if self.p & self.CARRY:
borrow = 0
else:
borrow = 1
# but the final result will be adjusted
nibble0 = (aluresult + adjust0) & 0xf
nibble1 = ((aluresult + adjust1) >> 4) & 0xf
result = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
self.p &= ~(self.CARRY + self.ZERO + self.OVERFLOW + self.NEGATIVE)
if ( (self.a ^ data) & (self.a ^ result) ) & self.NEGATIVE:
self.p |= self.OVERFLOW
data = result & self.byteMask
if data == 0:
self.p |= self.ZERO
if result > self.byteMask:
self.p |= self.CARRY
self.p |= data & self.NEGATIVE
self.a = data
self.p &= ~(self.CARRY + self.ZERO + self.NEGATIVE + self.OVERFLOW)
if aluresult == 0:
self.p |= self.ZERO
else:
self.p |= aluresult & self.NEGATIVE
if decimalcarry == 1:
self.p |= self.CARRY
if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE:
self.p |= self.OVERFLOW
self.a = (nibble1 << 4) + nibble0
else:
if self.p & self.CARRY:
borrow = 0
else:
borrow = 1
result = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
self.p &= ~(self.CARRY + self.ZERO + self.OVERFLOW + self.NEGATIVE)
if ((self.a ^ data) & (self.a ^ result)) & self.NEGATIVE:
self.p |= self.OVERFLOW
data = result & self.byteMask
if data == 0:
self.p |= self.ZERO
if result > self.byteMask:
self.p |= self.CARRY
self.p |= data & self.NEGATIVE
self.a = data
def opDECR(self, x):
if x is None:
@ -495,8 +497,8 @@ class MPU:
def inst_not_implemented(self):
self.pc += 1
instruct = [inst_not_implemented] * 256
cycletime = [0] * 256
instruct = [inst_not_implemented] * 256
cycletime = [0] * 256
extracycles = [0] * 256
disassemble = [('???', 'imp')] * 256
@ -505,7 +507,8 @@ class MPU:
@instruction(name="BRK", mode="imp", cycles=7)
def inst_0x00(self):
pc = (self.pc + 1) & self.addrMask # The pc has already been increased one
# pc has already been increased one
pc = (self.pc + 1) & self.addrMask
self.stPushWord(pc)
self.p |= self.BREAK
@ -592,8 +595,8 @@ class MPU:
@instruction(name="JSR", mode="abs", cycles=6)
def inst_0x20(self):
self.stPushWord((self.pc+1)&self.addrMask)
self.pc=self.WordAt(self.pc)
self.stPushWord((self.pc + 1) & self.addrMask)
self.pc = self.WordAt(self.pc)
@instruction(name="AND", mode="inx", cycles=6)
def inst_0x21(self):
@ -716,7 +719,7 @@ class MPU:
@instruction(name="JMP", mode="abs", cycles=3)
def inst_0x4c(self):
self.pc=self.WordAt(self.pc)
self.pc = self.WordAt(self.pc)
@instruction(name="EOR", mode="abs", cycles=4)
def inst_0x4d(self):
@ -754,7 +757,7 @@ class MPU:
@instruction(name="EOR", mode="aby", cycles=4, extracycles=1)
def inst_0x59(self):
self.opEOR(self.AbsoluteYAddr)
self.pc +=2
self.pc += 2
@instruction(name="EOR", mode="abx", cycles=4, extracycles=1)
def inst_0x5d(self):
@ -768,7 +771,7 @@ class MPU:
@instruction(name="RTS", mode="imp", cycles=6)
def inst_0x60(self):
self.pc=self.stPopWord()
self.pc = self.stPopWord()
self.pc += 1
@instruction(name="ADC", mode="inx", cycles=6)
@ -808,7 +811,7 @@ class MPU:
@instruction(name="ADC", mode="abs", cycles=4)
def inst_0x6d(self):
self.opADC(self.AbsoluteAddr)
self.pc +=2
self.pc += 2
@instruction(name="ROR", mode="abs", cycles=6)
def inst_0x6e(self):
@ -876,12 +879,12 @@ class MPU:
@instruction(name="DEY", mode="imp", cycles=2)
def inst_0x88(self):
self.y -= 1
self.y&=self.byteMask
self.y &= self.byteMask
self.FlagsNZ(self.y)
@instruction(name="TXA", mode="imp", cycles=2)
def inst_0x8a(self):
self.a=self.x
self.a = self.x
self.FlagsNZ(self.a)
@instruction(name="STY", mode="abs", cycles=4)
@ -935,7 +938,7 @@ class MPU:
@instruction(name="TXS", mode="imp", cycles=2)
def inst_0x9a(self):
self.sp=self.x
self.sp = self.x
@instruction(name="STA", mode="abx", cycles=5)
def inst_0x9d(self):
@ -1089,7 +1092,7 @@ class MPU:
@instruction(name="CMP", mode="imm", cycles=2)
def inst_0xc9(self):
self.opCMPR(self.ProgramCounter, self.a)
self.pc +=1
self.pc += 1
@instruction(name="DEX", mode="imp", cycles=2)
def inst_0xca(self):
@ -1177,8 +1180,8 @@ class MPU:
@instruction(name="INX", mode="imp", cycles=2)
def inst_0xe8(self):
self.x+=1
self.x&=self.byteMask
self.x += 1
self.x &= self.byteMask
self.FlagsNZ(self.x)
@instruction(name="SBC", mode="imm", cycles=2)

View File

@ -1,6 +1,7 @@
from py65.devices import mpu6502
from py65.utils.devices import make_instruction_decorator
class MPU(mpu6502.MPU):
def __init__(self, *args, **kwargs):
mpu6502.MPU.__init__(self, *args, **kwargs)
@ -15,18 +16,18 @@ class MPU(mpu6502.MPU):
return self
# Make copies of the lists
instruct = mpu6502.MPU.instruct[:]
cycletime = mpu6502.MPU.cycletime[:]
instruct = mpu6502.MPU.instruct[:]
cycletime = mpu6502.MPU.cycletime[:]
extracycles = mpu6502.MPU.extracycles[:]
disassemble = mpu6502.MPU.disassemble[:]
instruction = make_instruction_decorator(instruct, disassemble,
cycletime, extracycles)
cycletime, extracycles)
# addressing modes
def ZeroPageIndirectAddr(self):
return self.WordAt( 255 & (self.ByteAt(self.pc)))
return self.WordAt(255 & (self.ByteAt(self.pc)))
def AccumulatorAddr(self):
return self.a
@ -267,4 +268,3 @@ class MPU(mpu6502.MPU):
def inst_0xf2(self):
self.opSBC(self.ZeroPageIndirectAddr)
self.pc += 1

View File

@ -1,6 +1,7 @@
from py65.devices import mpu6502
from py65.utils.devices import make_instruction_decorator
class MPU(mpu6502.MPU):
"""
The 65Org16 is a derivative of the 6502 architecture
@ -10,21 +11,22 @@ class MPU(mpu6502.MPU):
- and otherwise all opcodes and addressing modes are like the NMOS 6502
- sign bit is bit 15, overflow bit is bit 14
One implementation can be found here: https://github.com/BigEd/verilog-6502/wiki
One implementation can be found here:
https://github.com/BigEd/verilog-6502/wiki
"""
BYTE_WIDTH = 16
BYTE_WIDTH = 16
BYTE_FORMAT = "%04x"
ADDR_WIDTH = 32
ADDR_WIDTH = 32
ADDR_FORMAT = "%08x"
def __init__(self, *args, **kwargs):
mpu6502.MPU.__init__(self, *args, **kwargs)
self.name = '65Org16'
self.waiting = False
self.IrqTo = (1<<self.ADDR_WIDTH)-2
self.ResetTo = (1<<self.ADDR_WIDTH)-4
self.NMITo = (1<<self.ADDR_WIDTH)-6
self.IrqTo = (1 << self.ADDR_WIDTH) - 2
self.ResetTo = (1 << self.ADDR_WIDTH) - 4
self.NMITo = (1 << self.ADDR_WIDTH) - 6
self.NEGATIVE = 1 << 15
self.OVERFLOW = 1 << 14
@ -36,12 +38,11 @@ class MPU(mpu6502.MPU):
return self
# Make copies of the lists
instruct = mpu6502.MPU.instruct[:]
cycletime = mpu6502.MPU.cycletime[:]
instruct = mpu6502.MPU.instruct[:]
cycletime = mpu6502.MPU.cycletime[:]
extracycles = mpu6502.MPU.extracycles[:]
disassemble = mpu6502.MPU.disassemble[:]
def reprformat(self):
return ("%s PC AC XR YR SP NV---------BDIZC\n" + \
"%s: %08x %04x %04x %04x %04x %s"
)
return ("%s PC AC XR YR SP NV---------BDIZC\n" +
"%s: %08x %04x %04x %04x %04x %s")

View File

@ -1,5 +1,6 @@
from py65.utils.addressing import AddressParser
class Disassembler:
def __init__(self, mpu, address_parser=None):
if address_parser is None:
@ -29,22 +30,22 @@ class Disassembler:
elif addressing == 'abs':
address = self._mpu.WordAt(pc + 1)
address_or_label = self._address_parser.label_for(address,
'$' + self.addrFmt % address)
address_or_label = self._address_parser.label_for(
address, '$' + self.addrFmt % address)
disasm += ' ' + address_or_label
length = 3
elif addressing == 'abx':
address = self._mpu.WordAt(pc + 1)
address_or_label = self._address_parser.label_for(address,
'$' + self.addrFmt % address)
address_or_label = self._address_parser.label_for(
address, '$' + self.addrFmt % address)
disasm += ' %s,X' % address_or_label
length = 3
elif addressing == 'aby':
address = self._mpu.WordAt(pc + 1)
address_or_label = self._address_parser.label_for(address,
'$' + self.addrFmt % address)
address_or_label = self._address_parser.label_for(
address, '$' + self.addrFmt % address)
disasm += ' %s,Y' % address_or_label
length = 3
@ -58,64 +59,64 @@ class Disassembler:
elif addressing == 'ind':
address = self._mpu.WordAt(pc + 1)
address_or_label = self._address_parser.label_for(address,
'$' + self.addrFmt % address)
address_or_label = self._address_parser.label_for(
address, '$' + self.addrFmt % address)
disasm += ' (%s)' % address_or_label
length = 3
elif addressing == 'iny':
zp_address = self._mpu.ByteAt(pc + 1)
address_or_label = self._address_parser.label_for(zp_address,
'$' + self.byteFmt % zp_address)
address_or_label = self._address_parser.label_for(
zp_address, '$' + self.byteFmt % zp_address)
disasm += ' (%s),Y' % address_or_label
length = 2
elif addressing == 'inx':
zp_address = self._mpu.ByteAt(pc + 1)
address_or_label = self._address_parser.label_for(zp_address,
'$' + self.byteFmt % zp_address)
address_or_label = self._address_parser.label_for(
zp_address, '$' + self.byteFmt % zp_address)
disasm += ' (%s,X)' % address_or_label
length = 2
elif addressing == 'rel':
opv = self._mpu.ByteAt(pc + 1)
targ = pc + 2
if opv & (1<<(self.byteWidth-1)):
if opv & (1 << (self.byteWidth - 1)):
targ -= (opv ^ self.byteMask) + 1
else:
targ += opv
targ &= self.addrMask
address_or_label = self._address_parser.label_for(targ,
'$' + self.addrFmt % targ)
address_or_label = self._address_parser.label_for(
targ, '$' + self.addrFmt % targ)
disasm += ' ' + address_or_label
length = 2
elif addressing == 'zpi':
zp_address = self._mpu.ByteAt(pc + 1)
address_or_label = self._address_parser.label_for(zp_address,
'($' + self.byteFmt % zp_address + ')' )
address_or_label = self._address_parser.label_for(
zp_address, '($' + self.byteFmt % zp_address + ')')
disasm += ' %s' % address_or_label
length = 2
elif addressing == 'zpg':
zp_address = self._mpu.ByteAt(pc + 1)
address_or_label = self._address_parser.label_for(zp_address,
'$' + self.byteFmt % zp_address)
address_or_label = self._address_parser.label_for(
zp_address, '$' + self.byteFmt % zp_address)
disasm += ' %s' % address_or_label
length = 2
elif addressing == 'zpx':
zp_address = self._mpu.ByteAt(pc + 1)
address_or_label = self._address_parser.label_for(zp_address,
'$' + self.byteFmt % zp_address)
address_or_label = self._address_parser.label_for(
zp_address, '$' + self.byteFmt % zp_address)
disasm += ' %s,X' % address_or_label
length = 2
elif addressing == 'zpy':
zp_address = self._mpu.ByteAt(pc + 1)
address_or_label = self._address_parser.label_for(zp_address,
'$' + self.byteFmt % zp_address)
address_or_label = self._address_parser.label_for(
zp_address, '$' + self.byteFmt % zp_address)
disasm += ' %s,Y' % address_or_label
length = 2

View File

@ -1,5 +1,6 @@
from collections import defaultdict
class ObservableMemory:
def __init__(self, subject=None, addrWidth=16):
self.physMask = 0xffff
@ -8,10 +9,10 @@ class ObservableMemory:
self.physMask = 0x3ffff
if subject is None:
subject = (self.physMask+1) * [0x00]
subject = (self.physMask + 1) * [0x00]
self._subject = subject
self._read_subscribers = defaultdict(list)
self._read_subscribers = defaultdict(list)
self._write_subscribers = defaultdict(list)
def __setitem__(self, address, value):
@ -59,4 +60,4 @@ class ObservableMemory:
def write(self, start_address, bytes):
start_address &= self.physMask
self._subject[start_address:start_address+len(bytes)] = bytes
self._subject[start_address:start_address + len(bytes)] = bytes

View File

@ -30,12 +30,15 @@ from py65.utils import console
from py65.utils.conversions import itoa
from py65.memory import ObservableMemory
class Monitor(cmd.Cmd):
Microprocessors = {'6502': NMOS6502, '65C02': CMOS65C02, '65Org16': V65Org16}
Microprocessors = {'6502': NMOS6502, '65C02': CMOS65C02,
'65Org16': V65Org16}
def __init__(self, mpu_type=NMOS6502, completekey='tab', stdin=None, stdout=None, argv=None):
self.mpu_type=mpu_type
def __init__(self, mpu_type=NMOS6502, completekey='tab', stdin=None,
stdout=None, argv=None):
self.mpu_type = mpu_type
if argv is None:
argv = sys.argv
self._reset(self.mpu_type)
@ -47,37 +50,44 @@ class Monitor(cmd.Cmd):
def _parse_args(self, argv):
try:
options, args = getopt.getopt(argv[1:], 'hm:l:r:g:',
['help', 'mpu=', 'load=', 'rom=', 'goto='])
shortopts = 'hm:l:r:g:'
longopts = ['help', 'mpu=', 'load=', 'rom=', 'goto=']
options, args = getopt.getopt(argv[1:], shortopts, longopts)
except getopt.GetoptError, err:
self._output(str(err))
self._usage()
self._exit(1)
for opt, value in options:
if opt in ('-l','--load'):
if opt in ('-l', '--load'):
cmd = "load %s" % value
self.onecmd(cmd)
if opt in ('-r','--rom'):
if opt in ('-r', '--rom'):
# load a ROM and run from the reset vector
cmd = "load %s %d" % (value, -1)
self.onecmd(cmd)
physMask = self._mpu.memory.physMask
reset = self._mpu.ResetTo & physMask
dest = self._mpu.memory[reset] + (self._mpu.memory[reset+1] << self.byteWidth)
dest = self._mpu.memory[reset] + \
(self._mpu.memory[reset + 1] << self.byteWidth)
cmd = "goto %08x" % dest
self.onecmd(cmd)
if opt in ('-g','--goto'):
if opt in ('-g', '--goto'):
cmd = "goto %s" % value
self.onecmd(cmd)
if opt in ('-m','--mpu'):
if opt in ('-m', '--mpu'):
if self._get_mpu(value) is None:
mpus = self.Microprocessors.keys()
mpus.sort()
self._output("Fatal: no such MPU. Available MPUs: %s" % ', '.join(mpus))
msg = "Fatal: no such MPU. Available MPUs: %s"
self._output(msg % ', '.join(mpus))
sys.exit(1)
cmd = "mpu %s" % value
self.onecmd(cmd)
elif opt in ("-h", "--help"):
self._usage()
self._exit(1)
@ -94,7 +104,7 @@ class Monitor(cmd.Cmd):
result = cmd.Cmd.onecmd(self, line)
except KeyboardInterrupt:
self._output("Interrupt")
except Exception,e:
except Exception, e:
(file, fun, line), t, v, tbinfo = compact_traceback()
error = 'Error: %s, %s: file: %s line: %s' % (t, v, file, line)
self._output(error)
@ -152,7 +162,7 @@ class Monitor(cmd.Cmd):
# special case for vice compatibility
if line.startswith('~'):
line = self._shortcuts['~'] + ' ' + line[1:]
line = self._shortcuts['~'] + ' ' + line[1:]
# command shortcuts
for shortcut, command in self._shortcuts.iteritems():
@ -279,7 +289,8 @@ class Monitor(cmd.Cmd):
end = start + len(bytes)
self._mpu.memory[start:end] = bytes
self.do_disassemble((self.addrFmt+":"+self.addrFmt) % (start, end))
rangestr = (self.addrFmt + ":" + self.addrFmt) % (start, end)
self.do_disassemble(rangestr)
except KeyError:
self._output("Bad label: %s" % args)
except OverflowError:
@ -304,9 +315,11 @@ class Monitor(cmd.Cmd):
return
while True:
prompt = "\r$" + ( self.addrFmt % start ) + " " + (" " * (1 + self.byteWidth/4) * 3)
prompt = "\r$" + (self.addrFmt % start) + " " + \
(" " * (1 + self.byteWidth / 4) * 3)
line = console.line_input(prompt,
stdin=self.stdin, stdout=self.stdout)
stdin=self.stdin, stdout=self.stdout)
if not line.strip():
self.stdout.write("\n")
@ -322,16 +335,20 @@ class Monitor(cmd.Cmd):
# print disassembly
bytes, disasm = self._disassembler.instruction_at(start)
disassembly = self._format_disassembly(start, bytes, disasm)
self.stdout.write("\r" + (' ' * (len(prompt+line) + 5) ) + "\r")
indent = ' ' * (len(prompt + line) + 5)
self.stdout.write("\r" + indent + "\r")
self.stdout.write(disassembly + "\n")
start += bytes
except KeyError:
self.stdout.write("\r$" + (self.addrFmt % start) + " ?Label\n")
addr = self.addrFmt % start
self.stdout.write("\r$%s ?Label\n" % addr)
except OverflowError:
self.stdout.write("\r$" + (self.addrFmt % start) + " ?Overflow\n")
addr = self.addrFmt % start
self.stdout.write("\r$%s ?Overflow\n" % addr)
except SyntaxError:
self.stdout.write("\r$" + (self.addrFmt % start) + " ?Syntax\n")
addr = self.addrFmt % start
self.stdout.write("\r$%s ?Syntax\n" % addr)
def do_disassemble(self, args):
split = shlex.split(args)
@ -350,10 +367,10 @@ class Monitor(cmd.Cmd):
def _format_disassembly(self, address, bytes, disasm):
mem = ''
for byte in self._mpu.memory[address:address+bytes]:
for byte in self._mpu.memory[address:address + bytes]:
mem += self.byteFmt % byte + " "
fieldwidth = 1 + (1 + self.byteWidth/4) * 3
fieldwidth = 1 + (1 + self.byteWidth / 4) * 3
fieldfmt = "%%-%ds" % fieldwidth
return "$" + self.addrFmt % address + " " + fieldfmt % mem + disasm
@ -376,7 +393,7 @@ class Monitor(cmd.Cmd):
self._output("before the next RTS or RTI is executed.")
def do_return(self, args):
returns = [0x60, 0x40] # RTS, RTI
returns = [0x60, 0x40] # RTS, RTI
self._run(stopcodes=returns)
def help_goto(self):
@ -385,7 +402,7 @@ class Monitor(cmd.Cmd):
def do_goto(self, args):
self._mpu.pc = self._address_parser.number(args)
brks = [0x00] # BRK
brks = [0x00] # BRK
self._run(stopcodes=brks)
def _run(self, stopcodes=[]):
@ -396,7 +413,7 @@ class Monitor(cmd.Cmd):
def help_radix(self):
self._output("radix [H|D|O|B]")
self._output("Set the default radix to hex, decimal, octal, or binary.")
self._output("Set default radix to hex, decimal, octal, or binary.")
self._output("With no argument, the current radix is printed.")
def help_cycles(self):
@ -424,7 +441,7 @@ class Monitor(cmd.Cmd):
def help_tilde(self):
self._output("~ <number>")
self._output("Display the specified number in decimal, hex, octal, and binary.")
self._output("Display a number in decimal, hex, octal, and binary.")
def do_tilde(self, args):
try:
@ -439,7 +456,7 @@ class Monitor(cmd.Cmd):
self._output(itoa(num, 2).zfill(8))
def help_registers(self):
self._output("registers[<reg_name> = <number> [, <reg_name> = <number>]*]")
self._output("registers[<name>=<value> [, <name>=<value>]*]")
self._output("Assign respective registers. With no parameters,")
self._output("display register values.")
@ -487,7 +504,7 @@ class Monitor(cmd.Cmd):
def help_load(self):
self._output("load \"filename\" <address>")
self._output("Load the specified file into memory at the specified address.")
self._output("Load a file into memory at the specified address.")
self._output("Commodore-style load address bytes are ignored.")
def do_load(self, args):
@ -524,12 +541,15 @@ class Monitor(cmd.Cmd):
# if the start address was -1, we load to top of memory
if start == -1:
start = self.addrMask - len(bytes)/(self.byteWidth/8) + 1
start = self.addrMask - len(bytes) / (self.byteWidth / 8) + 1
if self.byteWidth==8:
bytes=map(ord, bytes)
elif self.byteWidth==16:
bytes=map(lambda msb,lsb: (ord(msb)<<8)+ord(lsb),bytes[0::2],bytes[1::2])
if self.byteWidth == 8:
bytes = map(ord, bytes)
elif self.byteWidth == 16:
def format(msb, lsb):
return (ord(msb) << 8) + ord(lsb)
bytes = map(format, bytes[0::2], bytes[1::2])
self._fill(start, start, bytes)
@ -541,15 +561,15 @@ class Monitor(cmd.Cmd):
filename = split[0]
start = self._address_parser.number(split[1])
end = self._address_parser.number(split[2])
end = self._address_parser.number(split[2])
bytes = self._mpu.memory[start:end+1]
bytes = self._mpu.memory[start:end + 1]
try:
f = open(filename, 'wb')
for byte in bytes:
# output each octect from msb first
for shift in range (self.byteWidth-8,-1,-8):
f.write(chr((byte>>shift) & 0xff))
for shift in range(self.byteWidth - 8, -1, -8):
f.write(chr((byte >> shift) & 0xff))
f.close()
except (OSError, IOError), why:
msg = "Cannot save file: [%d] %s" % (why[0], why[1])
@ -560,14 +580,15 @@ class Monitor(cmd.Cmd):
def help_save(self):
self._output("save \"filename\" <start> <end>")
self._output("Save the specified memory range to disk as a binary file.")
self._output("Save the specified memory range as a binary file.")
self._output("Commodore-style load address bytes are not written.")
def help_fill(self):
self._output("fill <address_range> <data_list>")
self._output("Fill memory in the specified address range with the data in")
self._output("<data_list>. If the size of the address range is greater")
self._output("than the size of the data_list, the data_list is repeated.")
self._output("Fill memory in the address range with the data in")
self._output("<data_list>. If the size of the address range is")
self._output("greater than the size of the data_list, the data_list ")
self._output("is repeated.")
def do_fill(self, args):
split = shlex.split(args)
@ -613,7 +634,7 @@ class Monitor(cmd.Cmd):
start, end = self._address_parser.range(split[0])
line = self.addrFmt % start + ":"
for address in range(start, end+1):
for address in range(start, end + 1):
byte = self._mpu.memory[address]
more = " " + self.byteFmt % byte
@ -635,7 +656,7 @@ class Monitor(cmd.Cmd):
return self.help_add_label()
address = self._address_parser.number(split[0])
label = split[1]
label = split[1]
self._address_parser.labels[label] = address
@ -681,6 +702,7 @@ class Monitor(cmd.Cmd):
self._output("Set the width used by some commands to wrap output.")
self._output("With no argument, the current width is printed.")
def main(args=None):
c = Monitor()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ from py65.devices.mpu65c02 import MPU as MPU65C02
from py65.assembler import Assembler
from py65.utils.addressing import AddressParser
class AssemblerTests(unittest.TestCase):
def test_assemble_bad_syntax_raises_syntaxerror(self):
self.assertRaises(SyntaxError,
@ -955,6 +956,7 @@ class AssemblerTests(unittest.TestCase):
assembler = Assembler(mpu, address_parser)
return assembler.assemble(statement, pc)
def test_suite():
return unittest.findTestCases(sys.modules[__name__])

View File

@ -5,6 +5,7 @@ from py65.devices.mpu65c02 import MPU as MPU65C02
from py65.disassembler import Disassembler
from py65.utils.addressing import AddressParser
class DisassemblerTests(unittest.TestCase):
def test_disassembles_00(self):
length, disasm = self.disassemble([0x00])
@ -1304,9 +1305,10 @@ class DisassemblerTests(unittest.TestCase):
mpu = MPU()
address_parser = AddressParser()
disasm = Disassembler(mpu, address_parser)
mpu.memory[pc:len(bytes)-1] = bytes
mpu.memory[pc:len(bytes) - 81] = bytes
return disasm.instruction_at(pc)
def test_suite():
return unittest.findTestCases(sys.modules[__name__])

View File

@ -4,6 +4,7 @@ import re
import os
from py65.memory import ObservableMemory
class ObservableMemoryTests(unittest.TestCase):
# __setitem__
@ -56,7 +57,7 @@ class ObservableMemoryTests(unittest.TestCase):
def read_subscriber(address, value):
return 0xAB
mem.subscribe_to_read(xrange(0xC000, 0xC001+1), read_subscriber)
mem.subscribe_to_read(xrange(0xC000, 0xC001 + 1), read_subscriber)
mem[0xC000] = 0xAB
mem[0xC001] = 0xAB
@ -68,6 +69,7 @@ class ObservableMemoryTests(unittest.TestCase):
mem = ObservableMemory(subject=subject)
calls = []
def read_subscriber(address):
calls.append('read_subscriber')
@ -107,6 +109,7 @@ class ObservableMemoryTests(unittest.TestCase):
mem = ObservableMemory(subject=subject)
calls = []
def read_subscriber_1(address):
calls.append('read_subscriber_1')
return 0x01
@ -139,7 +142,7 @@ class ObservableMemoryTests(unittest.TestCase):
def write_subscriber(address, value):
return 0xFF
mem.subscribe_to_write([0xC000,0xC001], write_subscriber)
mem.subscribe_to_write([0xC000, 0xC001], write_subscriber)
mem.write(0xC000, [0x01, 002])
self.assertEqual(0x01, subject[0xC000])

View File

@ -6,6 +6,7 @@ import tempfile
from py65.monitor import Monitor
from StringIO import StringIO
class MonitorTests(unittest.TestCase):
# line processing
@ -213,7 +214,7 @@ class MonitorTests(unittest.TestCase):
def test_do_cycles_shows_count_after_step(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0x0000] = 0xEA #=> NOP (2 cycles)
mon._mpu.memory[0x0000] = 0xEA # => NOP (2 cycles)
mon._mpu.step()
mon.do_cycles("")
@ -249,7 +250,7 @@ class MonitorTests(unittest.TestCase):
mon = Monitor(stdout=stdout)
mon._address_parser.labels['foo'] = 0xc000
mon.do_delete_label('foo')
self.assertFalse(mon._address_parser.labels.has_key('foo'))
self.assertFalse('foo' in mon._address_parser.labels)
out = stdout.getvalue()
self.assertEqual('', out)
@ -280,7 +281,7 @@ class MonitorTests(unittest.TestCase):
def test_disassemble_will_disassemble_one_address(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xc000] = 0xEA #=> NOP
mon._mpu.memory[0xc000] = 0xEA # => NOP
mon._mpu.step()
mon.do_disassemble("c000")
@ -291,8 +292,8 @@ class MonitorTests(unittest.TestCase):
def test_disassemble_will_disassemble_an_address_range(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xc000] = 0xEA #=> NOP
mon._mpu.memory[0xc001] = 0xEA #=> NOP
mon._mpu.memory[0xc000] = 0xEA # => NOP
mon._mpu.memory[0xc001] = 0xEA # => NOP
mon._mpu.step()
mon.do_disassemble("c000:c002")
@ -462,7 +463,8 @@ class MonitorTests(unittest.TestCase):
mon.do_load("'%s' a600" % filename)
self.assertEqual('Wrote +3 bytes from $a600 to $a602\n',
stdout.getvalue())
self.assertEqual([0xAA, 0xBB, 0xCC], mon._mpu.memory[0xA600:0xA603])
self.assertEqual([0xAA, 0xBB, 0xCC],
mon._mpu.memory[0xA600:0xA603])
finally:
os.unlink(filename)
@ -632,7 +634,6 @@ class MonitorTests(unittest.TestCase):
out = stdout.getvalue()
self.assertEqual("%s\n" % os.getcwd(), out)
def test_help_pwd(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
@ -714,7 +715,7 @@ class MonitorTests(unittest.TestCase):
mon = Monitor(stdout=stdout)
mon.help_registers()
out = stdout.getvalue()
self.assertTrue(out.startswith("registers[<reg_name>"))
self.assertTrue(out.startswith("registers[<name>"))
# return

View File

@ -2,6 +2,7 @@ import unittest
import sys
from py65.utils.addressing import AddressParser
class AddressParserTests(unittest.TestCase):
def test_maxwidth_can_be_set_in_constructor(self):
parser = AddressParser(maxwidth=24)
@ -122,7 +123,7 @@ class AddressParserTests(unittest.TestCase):
self.assertRaises(OverflowError, parser.number, 'foo+5')
def test_label_for_returns_label(self):
parser = AddressParser(labels={'chrout':0xFFD2})
parser = AddressParser(labels={'chrout': 0xFFD2})
self.assertEqual('chrout', parser.label_for(0xFFD2))
def test_label_for_returns_none_by_default(self):

View File

@ -2,6 +2,7 @@ import sys
import unittest
from py65.utils.console import getch
class ConsoleTopLevelTests(unittest.TestCase):
pass

View File

@ -2,6 +2,7 @@ import sys
import unittest
from py65.utils.conversions import itoa
class ConversionsTopLevelTests(unittest.TestCase):
def test_itoa_decimal_output(self):
self.assertEqual('10', itoa(10, base=10))

View File

@ -2,6 +2,7 @@ import unittest
import sys
from py65.utils.hexdump import load, Loader
class TopLevelHexdumpTests(unittest.TestCase):
def test_load(self):
text = 'c000: aa bb'
@ -9,6 +10,7 @@ class TopLevelHexdumpTests(unittest.TestCase):
self.assertEqual(0xC000, start)
self.assertEqual([0xAA, 0xBB], data)
class HexdumpLoaderTests(unittest.TestCase):
def test_empty_string_does_nothing(self):
text = ''
@ -126,7 +128,6 @@ class HexdumpLoaderTests(unittest.TestCase):
self.assertEqual([0xAA, 0xBB], load.data)
def test_suite():
return unittest.findTestCases(sys.modules[__name__])

View File

@ -1,5 +1,6 @@
import re
class AddressParser(object):
"""Parse user input into addresses or ranges of addresses.
"""

View File

@ -4,11 +4,11 @@ if sys.platform[:3] == "win":
import msvcrt
def getch(stdin):
""" Read one character from the Windows console, blocking until one
is available. Does not echo the character. The stdin argument is
for function signature compatibility and is ignored.
"""
return msvcrt.getch()
""" Read one character from the Windows console, blocking until one
is available. Does not echo the character. The stdin argument is
for function signature compatibility and is ignored.
"""
return msvcrt.getch()
def getch_noblock(stdin):
""" Read one character from the Windows console without blocking.
@ -26,19 +26,19 @@ else:
import fcntl
def getch(stdin):
""" Read one character from stdin, blocking until one is available.
Does not echo the character.
"""
fd = stdin.fileno()
oldattr = termios.tcgetattr(fd)
newattr = oldattr[:]
newattr[3] &= ~termios.ICANON & ~termios.ECHO
try:
termios.tcsetattr(fd, termios.TCSANOW, newattr)
char = stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldattr)
return char
""" Read one character from stdin, blocking until one is available.
Does not echo the character.
"""
fd = stdin.fileno()
oldattr = termios.tcgetattr(fd)
newattr = oldattr[:]
newattr[3] &= ~termios.ICANON & ~termios.ECHO
try:
termios.tcsetattr(fd, termios.TCSANOW, newattr)
char = stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldattr)
return char
def getch_noblock(stdin):
""" Read one character from stdin without blocking. Does not echo the
@ -63,7 +63,7 @@ else:
if char == "\n":
char = "\r"
else:
char = ''
char = ''
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
@ -79,18 +79,18 @@ def line_input(prompt='', stdin=sys.stdin, stdout=sys.stdout):
stdout.write(prompt)
line = ''
while True:
char = getch(stdin)
code = ord(char)
if char in ("\n", "\r"):
break
elif code in (0x7f, 0x08): # backspace
if len(line) > 0:
line = line[:-1]
stdout.write("\r%s\r%s%s" % \
(' ' * (len(prompt+line) +5), prompt, line))
elif code == 0x1b: # escape
pass
else:
line += char
stdout.write(char)
char = getch(stdin)
code = ord(char)
if char in ("\n", "\r"):
break
elif code in (0x7f, 0x08): # backspace
if len(line) > 0:
line = line[:-1]
stdout.write("\r%s\r%s%s" %
(' ' * (len(prompt + line) + 5), prompt, line))
elif code == 0x1b: # escape
pass
else:
line += char
stdout.write(char)
return line

View File

@ -1,53 +1,59 @@
def itoa(num, base=10):
""" Convert a decimal number to its equivalent in another base.
This is essentially the inverse of int(num, base).
"""
negative = num < 0
if negative:
num = -num
num = -num
digits = []
while num > 0:
num, last_digit = divmod(num, base)
digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[last_digit])
num, last_digit = divmod(num, base)
digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[last_digit])
if negative:
digits.append('-')
digits.append('-')
digits.reverse()
return ''.join(digits)
def convert_to_bin(bcd):
return bcd2bin[bcd]
def convert_to_bcd(bin):
return bin2bcd[bin]
bcd2bin = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, # 0x00
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, # 0x10
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, # 0x20
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, # 0x30
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, # 0x40
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, # 0x50
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, # 0x60
70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, # 0x70
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, # 0x80
90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105, # 0x90
100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115, # 0xA0
110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, # 0xB0
120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135, # 0xC0
130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, # 0xD0
140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, # 0xE0
150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165 # 0xF0
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 30, 31, 32, 33, 34, 35, 36, 37,
38, 39, 40, 41, 42, 43, 44, 45, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
62, 63, 64, 65, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
74, 75, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 90, 91,
92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 100, 101,
102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
124, 125, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
132, 133, 134, 135, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 140, 141, 142, 143, 144, 145, 146, 147,
148, 149, 150, 151, 152, 153, 154, 155, 150, 151, 152, 153, 154, 155,
156, 157, 158, 159, 160, 161, 162, 163, 164, 165
]
bin2bcd = [
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
]

View File

@ -1,4 +1,5 @@
def make_instruction_decorator(instruct, disasm, allcycles, allextras):
def instruction(name, mode, cycles, extracycles=0):
def decorate(f):
@ -7,6 +8,6 @@ def make_instruction_decorator(instruct, disasm, allcycles, allextras):
disasm[opcode] = (name, mode)
allcycles[opcode] = cycles
allextras[opcode] = extracycles
return f # Return the original function
return f # Return the original function
return decorate
return instruction

View File

@ -1,9 +1,11 @@
from binascii import a2b_hex
def load(text):
load = Loader(text)
return (load.start_address, load.data)
class Loader:
def __init__(self, text):
self.load(text)
@ -42,15 +44,15 @@ class Loader:
def _parse_address(self, piece):
try:
addr_bytes = [ ord(c) for c in a2b_hex(piece) ]
addr_bytes = [ord(c) for c in a2b_hex(piece)]
except (TypeError, ValueError):
msg = "Could not parse address: %s" % piece
raise ValueError, msg
raise ValueError(msg)
if len(addr_bytes) != 2:
msg = "Expected address to be 2 bytes, got %d" % (
len(addr_bytes))
raise ValueError, msg
len(addr_bytes))
raise ValueError(msg)
address = (addr_bytes[0] << 8) + addr_bytes[1]
@ -61,20 +63,20 @@ class Loader:
elif address != (self.current_address):
msg = "Non-contigous block detected. Expected next address " \
"to be $%04x, label was $%04x" % (self.current_address,
address)
raise ValueError, msg
address)
raise ValueError(msg)
def _parse_bytes(self, piece):
if self.start_address is None:
msg = "Start address was not found in data"
raise ValueError, msg
raise ValueError(msg)
else:
try:
bytes = [ ord(c) for c in a2b_hex(piece) ]
bytes = [ord(c) for c in a2b_hex(piece)]
except (TypeError, ValueError):
msg = "Could not parse data: %s" % piece
raise ValueError, msg
raise ValueError(msg)
self.current_address += len(bytes)
self.data.extend(bytes)

View File

@ -30,31 +30,31 @@ CLASSIFIERS = [
'Topic :: Software Development :: Interpreters',
'Topic :: System :: Emulators',
'Topic :: System :: Hardware'
]
]
setup(
name = 'py65',
version = __version__,
license = 'License :: OSI Approved :: BSD License',
url = 'https://github.com/mnaberez/py65',
description = '6502 microprocessor simulation package',
long_description= DESC,
classifiers = CLASSIFIERS,
author = "Mike Naberezny",
author_email = "mike@naberezny.com",
maintainer = "Mike Naberezny",
maintainer_email = "mike@naberezny.com",
packages = find_packages(),
install_requires = [],
extras_require = {},
tests_require = [],
include_package_data = True,
zip_safe = False,
namespace_packages = ['py65'],
test_suite = "py65.tests",
entry_points = {
'console_scripts': [
'py65mon = py65.monitor:main',
],
},
name='py65',
version=__version__,
license='License :: OSI Approved :: BSD License',
url='https://github.com/mnaberez/py65',
description='6502 microprocessor simulation package',
long_description=DESC,
classifiers=CLASSIFIERS,
author="Mike Naberezny",
author_email="mike@naberezny.com",
maintainer="Mike Naberezny",
maintainer_email="mike@naberezny.com",
packages=find_packages(),
install_requires=[],
extras_require={},
tests_require=[],
include_package_data=True,
zip_safe=False,
namespace_packages=['py65'],
test_suite="py65.tests",
entry_points={
'console_scripts': [
'py65mon = py65.monitor:main',
],
},
)