PageRenderTime 59ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/src/py65/assembler.py

http://github.com/mnaberez/py65
Python | 135 lines | 110 code | 14 blank | 11 comment | 13 complexity | 27c04b3b8e27d6972e533dbfe77d1ae8 MD5 | raw file
  1. import re
  2. from py65.devices.mpu6502 import MPU
  3. from py65.utils.addressing import AddressParser
  4. class Assembler:
  5. Statement = re.compile(r'^([A-z]{3}\s+'
  6. r'\(?\s*)([^,\s\)]+)(\s*[,xXyY\s]*\)?'
  7. r'[,xXyY\s]*)$')
  8. Addressing8 = [
  9. ['zpi', re.compile(r'^\(\$00([0-9A-F]{2})\)$')], # "($0012)"
  10. ['zpx', re.compile(r'^\$00([0-9A-F]{2}),X$')], # "$0012,X"
  11. ['zpy', re.compile(r'^\$00([0-9A-F]{2}),Y$')], # "$0012,Y"
  12. ['zpg', re.compile(r'^\$00([0-9A-F]{2})$')], # "$0012"
  13. ['inx', re.compile(r'^\(\$00([0-9A-F]{2}),X\)$')], # "($0012,X)"
  14. ['iny', re.compile(r'^\(\$00([0-9A-F]{2})\),Y$')], # "($0012),Y"
  15. ['ind', re.compile(r'^\(\$([0-9A-F]{2})([0-9A-F]{2})\)$')], # "($1234)"
  16. ['abx', re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2}),X$')], # "$1234,X"
  17. ['aby', re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2}),Y$')], # "$1234,Y"
  18. ['abs', re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2})$')], # "$1234"
  19. ['rel', re.compile(r'^\$([0-9A-F]{2})([0-9A-F]{2})$')], # "$1234"
  20. ['imp', re.compile(r'^$')], # ""
  21. ['acc', re.compile(r'^$')], # ""
  22. ['acc', re.compile(r'^A$')], # "A"
  23. ['imm', re.compile(r'^#\$([0-9A-F]{2})$')] # "#$12"
  24. ]
  25. Addressing16 = [
  26. ['zpi', re.compile(r'^\(\$0000([0-9A-F]{4})\)$')], # "($00001234)"
  27. ['zpx', re.compile(r'^\$0000([0-9A-F]{4}),X$')], # "$00001234,X"
  28. ['zpy', re.compile(r'^\$0000([0-9A-F]{4}),Y$')], # "$00001234,Y"
  29. ['zpg', re.compile(r'^\$0000([0-9A-F]{4})$')], # "$00001234"
  30. ['inx', re.compile(r'^\(\$0000([0-9A-F]{4}),X\)$')], # "($00001234,X)"
  31. ['iny', re.compile(r'^\(\$0000([0-9A-F]{4})\),Y$')], # "($00001234),Y"
  32. ['ind', re.compile(r'^\(\$([0-9A-F]{4})([0-9A-F]{4})\)$')], # "($12345678)"
  33. ['abx', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4}),X$')], # "$12345678,X"
  34. ['aby', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4}),Y$')], # "$12345678,Y"
  35. ['abs', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4})$')], # "$12345678"
  36. ['rel', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4})$')], # "$12345678"
  37. ['imp', re.compile(r'^$')], # ""
  38. ['acc', re.compile(r'^$')], # ""
  39. ['acc', re.compile(r'^A$')], # "A"
  40. ['imm', re.compile(r'^#\$([0-9A-F]{4})$')] # "#$1234"
  41. ]
  42. Addressing = Addressing8
  43. def __init__(self, mpu, address_parser=None):
  44. """ If a configured AddressParser is passed, symbolic addresses
  45. may be used in the assembly statements.
  46. """
  47. if address_parser is None:
  48. address_parser = AddressParser()
  49. self._mpu = mpu
  50. self._address_parser = address_parser
  51. self.addrWidth = mpu.ADDR_WIDTH
  52. self.byteWidth = mpu.BYTE_WIDTH
  53. self.addrFmt = mpu.ADDR_FORMAT
  54. self.byteFmt = mpu.BYTE_FORMAT
  55. self.addrMask = mpu.addrMask
  56. self.byteMask = mpu.byteMask
  57. if self.byteWidth == 8:
  58. self.Addressing = self.Addressing8
  59. else:
  60. self.Addressing = self.Addressing16
  61. def assemble(self, statement, pc=0000):
  62. """ Assemble the given assembly language statement. If the statement
  63. uses relative addressing, the program counter (pc) must also be given.
  64. The result is a list of bytes, or None if the assembly failed.
  65. """
  66. opcode, operands = self.normalize_and_split(statement)
  67. for mode, pattern in self.Addressing:
  68. match = pattern.match(operands)
  69. if match:
  70. try:
  71. bytes = [ self._mpu.disassemble.index((opcode, mode)) ]
  72. except ValueError:
  73. continue
  74. operands = match.groups()
  75. if mode == 'rel':
  76. # relative branch
  77. absolute = int(''.join(operands), 16)
  78. relative = (absolute - pc) - 2
  79. relative = relative & self.byteMask
  80. operands = [ (self.byteFmt % relative) ]
  81. elif len(operands) == 2:
  82. # swap bytes
  83. operands = (operands[1], operands[0])
  84. operands = [ int(hex, 16) for hex in operands ]
  85. bytes.extend(operands)
  86. return bytes
  87. # assembly failed
  88. return None
  89. def normalize_and_split(self, statement):
  90. """ Given an assembly language statement like "lda $c12,x", normalize
  91. the statement by uppercasing it, removing unnecessary whitespace,
  92. and parsing the address part using AddressParser. The result of
  93. the normalization is a tuple of two strings (opcode, operands).
  94. """
  95. # normalize target in operand
  96. match = self.Statement.match(statement)
  97. if match:
  98. before, target, after = match.groups()
  99. # target is an immediate number
  100. if target.startswith('#'):
  101. number = self._address_parser.number(target[1:])
  102. if (number < 0x00) or (number > self.byteMask):
  103. raise OverflowError
  104. statement = before + '#$' + self.byteFmt % number
  105. # target is the accumulator
  106. elif target in ('a', 'A'):
  107. pass
  108. # target is an address or label
  109. else:
  110. address = self._address_parser.number(target)
  111. statement = before + '$' + self.addrFmt % address + after
  112. # strip unnecessary whitespace
  113. opcode = statement[:3].upper()
  114. operand = ''.join(statement[3:].split()).upper().strip()
  115. return (opcode, operand)