1
0
mirror of https://github.com/mnaberez/py65.git synced 2024-12-28 15:29:40 +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,7 +167,8 @@ 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'),

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

View File

@ -1,6 +1,7 @@
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
@ -11,7 +12,7 @@ class MPU:
NEGATIVE = 128
OVERFLOW = 64
UNUSED = 32
BREAK = 16 # there is no BREAK flag, but this position indicates BREAK
BREAK = 16 # there is no actual BREAK flag but this indicates BREAK
DECIMAL = 8
INTERRUPT = 4
ZERO = 2
@ -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')
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]
@ -222,7 +222,7 @@ class MPU:
tbyte = tbyte >> 1
if tbyte:
pass # {}
pass
else:
self.p |= self.ZERO
@ -352,7 +352,7 @@ class MPU:
if self.p & self.CARRY:
if tbyte & 1:
pass # {}
pass
else:
self.p &= ~self.CARRY
tbyte = (tbyte >> 1) | self.NEGATIVE
@ -403,7 +403,9 @@ class MPU:
adjust1 = 10 << 4
# the ALU outputs are not decimally adjusted
aluresult = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
aluresult = self.a + (~data & self.byteMask) + \
(self.p & self.CARRY)
if aluresult > self.byteMask:
decimalcarry = 1
aluresult &= self.byteMask
@ -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

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)
@ -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,7 +11,8 @@ 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
@ -42,6 +44,5 @@ class MPU(mpu6502.MPU):
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,22 +59,22 @@ 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
@ -86,36 +87,36 @@ class Disassembler:
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

View File

@ -30,11 +30,14 @@ 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):
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
@ -47,8 +50,9 @@ 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()
@ -58,26 +62,32 @@ class Monitor(cmd.Cmd):
if opt in ('-l', '--load'):
cmd = "load %s" % value
self.onecmd(cmd)
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'):
cmd = "goto %s" % value
self.onecmd(cmd)
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)
@ -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,7 +315,9 @@ 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)
@ -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)
@ -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):
@ -528,8 +545,11 @@ class Monitor(cmd.Cmd):
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])
def format(msb, lsb):
return (ord(msb) << 8) + ord(lsb)
bytes = map(format, bytes[0::2], bytes[1::2])
self._fill(start, start, bytes)
@ -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)
@ -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__
@ -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

View File

@ -6,6 +6,7 @@ import tempfile
from py65.monitor import Monitor
from StringIO import StringIO
class MonitorTests(unittest.TestCase):
# line processing
@ -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)
@ -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)

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

@ -86,7 +86,7 @@ def line_input(prompt='', stdin=sys.stdin, stdout=sys.stdout):
elif code in (0x7f, 0x08): # backspace
if len(line) > 0:
line = line[:-1]
stdout.write("\r%s\r%s%s" % \
stdout.write("\r%s\r%s%s" %
(' ' * (len(prompt + line) + 5), prompt, line))
elif code == 0x1b: # escape
pass

View File

@ -1,3 +1,5 @@
def itoa(num, base=10):
""" Convert a decimal number to its equivalent in another base.
This is essentially the inverse of int(num, base).
@ -14,31 +16,35 @@ def itoa(num, base=10):
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,

View File

@ -1,4 +1,5 @@
def make_instruction_decorator(instruct, disasm, allcycles, allextras):
def instruction(name, mode, cycles, extracycles=0):
def decorate(f):

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)
@ -45,12 +47,12 @@ class Loader:
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
raise ValueError(msg)
address = (addr_bytes[0] << 8) + addr_bytes[1]
@ -62,19 +64,19 @@ class Loader:
msg = "Non-contigous block detected. Expected next address " \
"to be $%04x, label was $%04x" % (self.current_address,
address)
raise ValueError, msg
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)]
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)