2017-12-11 10:42:42 +00:00
|
|
|
"""Binary machine code emitter. Used in SixtyPical to emit 6502 machine code,
|
|
|
|
but not specific to SixtyPical, or 6502. Not even necessarily machine code -
|
|
|
|
though some parts are written around the assumptions of 8-bit architectures."""
|
|
|
|
|
|
|
|
|
2015-10-17 10:08:25 +00:00
|
|
|
class Emittable(object):
|
|
|
|
def size(self):
|
2015-10-17 15:51:35 +00:00
|
|
|
raise NotImplementedError
|
2015-10-17 10:08:25 +00:00
|
|
|
|
2015-10-17 15:51:35 +00:00
|
|
|
def serialize(self, addr):
|
2018-09-06 17:11:47 +00:00
|
|
|
"""Should return an array of unsigned bytes (integers from 0 to 255.)
|
|
|
|
`addr` is the address the value is being serialized at; for most objects
|
|
|
|
it makes no difference, but some objects (like relative branches) do care."""
|
2015-10-17 10:08:25 +00:00
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
class Byte(Emittable):
|
2015-10-16 21:59:35 +00:00
|
|
|
def __init__(self, value):
|
2018-09-05 20:45:58 +00:00
|
|
|
if isinstance(value, str):
|
2018-03-29 10:09:02 +00:00
|
|
|
value = ord(value)
|
2015-10-17 10:08:25 +00:00
|
|
|
if value < -127 or value > 255:
|
2015-10-20 08:33:30 +00:00
|
|
|
raise IndexError(value)
|
2015-10-17 10:08:25 +00:00
|
|
|
if value < 0:
|
|
|
|
value += 256
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
def size(self):
|
|
|
|
return 1
|
|
|
|
|
2018-09-06 17:11:47 +00:00
|
|
|
def serialize(self, addr):
|
2018-09-06 15:18:41 +00:00
|
|
|
return [self.value]
|
2015-10-17 10:08:25 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "%s(%r)" % (self.__class__.__name__, self.value)
|
|
|
|
|
|
|
|
|
|
|
|
class Word(Emittable):
|
|
|
|
def __init__(self, value):
|
|
|
|
# TODO: range-checking
|
2015-10-16 21:59:35 +00:00
|
|
|
self.value = value
|
|
|
|
|
|
|
|
def size(self):
|
|
|
|
return 2
|
|
|
|
|
2018-09-06 17:11:47 +00:00
|
|
|
def serialize(self, addr):
|
2015-10-16 21:59:35 +00:00
|
|
|
word = self.value
|
|
|
|
low = word & 255
|
|
|
|
high = (word >> 8) & 255
|
2018-09-06 15:18:41 +00:00
|
|
|
return [low, high]
|
2015-10-16 21:59:35 +00:00
|
|
|
|
2015-10-17 10:08:25 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return "%s(%r)" % (self.__class__.__name__, self.value)
|
|
|
|
|
2015-10-16 21:59:35 +00:00
|
|
|
|
2017-12-08 16:59:31 +00:00
|
|
|
class Table(Emittable):
|
2018-02-02 16:31:23 +00:00
|
|
|
def __init__(self, value, size):
|
2018-03-29 10:09:02 +00:00
|
|
|
"""`value` should be an iterable of Emittables."""
|
2017-12-12 16:41:49 +00:00
|
|
|
# TODO: range-checking
|
|
|
|
self.value = value
|
2018-02-02 16:31:23 +00:00
|
|
|
self._size = size
|
2017-12-12 16:41:49 +00:00
|
|
|
|
2017-12-08 16:59:31 +00:00
|
|
|
def size(self):
|
2018-02-02 16:31:23 +00:00
|
|
|
return self._size
|
2017-12-08 16:59:31 +00:00
|
|
|
|
2018-09-06 17:11:47 +00:00
|
|
|
def serialize(self, addr):
|
2018-09-06 15:18:41 +00:00
|
|
|
buf = []
|
|
|
|
for emittable in self.value:
|
2018-09-06 17:11:47 +00:00
|
|
|
buf.extend(emittable.serialize(addr)) # FIXME: addr + offset
|
2018-03-29 10:09:02 +00:00
|
|
|
while len(buf) < self.size():
|
2018-09-06 15:18:41 +00:00
|
|
|
buf.append(0)
|
2018-03-29 10:09:02 +00:00
|
|
|
return buf
|
2017-12-08 16:59:31 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "%s()" % (self.__class__.__name__)
|
|
|
|
|
|
|
|
|
2015-10-17 10:08:25 +00:00
|
|
|
class Label(Emittable):
|
2017-12-11 10:42:42 +00:00
|
|
|
def __init__(self, name, addr=None, length=None):
|
2015-10-16 21:59:35 +00:00
|
|
|
self.name = name
|
|
|
|
self.addr = addr
|
2017-12-11 10:42:42 +00:00
|
|
|
self.length = length
|
2015-10-16 21:59:35 +00:00
|
|
|
|
|
|
|
def set_addr(self, addr):
|
|
|
|
self.addr = addr
|
|
|
|
|
2017-12-11 10:42:42 +00:00
|
|
|
def set_length(self, length):
|
|
|
|
self.length = length
|
|
|
|
|
2015-10-16 21:59:35 +00:00
|
|
|
def size(self):
|
|
|
|
return 2
|
|
|
|
|
2018-09-06 17:11:47 +00:00
|
|
|
def serialize(self, addr, offset=0):
|
2015-10-17 10:08:25 +00:00
|
|
|
assert self.addr is not None, "unresolved label: %s" % self.name
|
2018-09-06 17:11:47 +00:00
|
|
|
return Word(self.addr + offset).serialize(addr)
|
2015-10-16 21:59:35 +00:00
|
|
|
|
2015-10-17 17:11:23 +00:00
|
|
|
def serialize_relative_to(self, addr):
|
|
|
|
assert self.addr is not None, "unresolved label: %s" % self.name
|
2018-09-06 17:11:47 +00:00
|
|
|
return Byte(self.addr - (addr + 2)).serialize(addr)
|
2015-10-17 17:11:23 +00:00
|
|
|
|
2018-09-06 17:11:47 +00:00
|
|
|
def serialize_as_zero_page(self, addr, offset=0):
|
2017-11-24 13:09:10 +00:00
|
|
|
assert self.addr is not None, "unresolved label: %s" % self.name
|
2018-09-06 17:11:47 +00:00
|
|
|
return Byte(self.addr + offset).serialize(addr)
|
2017-11-24 13:09:10 +00:00
|
|
|
|
2015-10-17 14:06:50 +00:00
|
|
|
def __repr__(self):
|
2017-12-11 10:42:42 +00:00
|
|
|
addr_s = ', addr=%r' % self.addr if self.addr is not None else ''
|
|
|
|
length_s = ', length=%r' % self.length if self.length is not None else ''
|
|
|
|
return "%s(%r%s%s)" % (self.__class__.__name__, self.name, addr_s, length_s)
|
2015-10-19 07:55:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Offset(Emittable):
|
|
|
|
def __init__(self, label, offset):
|
|
|
|
assert isinstance(label, Label)
|
|
|
|
self.label = label
|
|
|
|
self.offset = offset
|
|
|
|
|
|
|
|
def size(self):
|
|
|
|
self.label.size()
|
|
|
|
|
2018-09-06 17:11:47 +00:00
|
|
|
def serialize(self, addr):
|
|
|
|
return self.label.serialize(addr, offset=self.offset)
|
2015-10-19 07:55:47 +00:00
|
|
|
|
2018-09-06 17:11:47 +00:00
|
|
|
def serialize_as_zero_page(self, addr, offset=0):
|
|
|
|
return self.label.serialize_as_zero_page(addr, offset=self.offset)
|
2017-11-24 13:42:14 +00:00
|
|
|
|
2015-10-19 07:55:47 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return "%s(%r, %r)" % (self.__class__.__name__, self.label, self.offset)
|
2015-10-22 15:20:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
class HighAddressByte(Emittable):
|
|
|
|
def __init__(self, label):
|
|
|
|
assert isinstance(label, Label)
|
|
|
|
self.label = label
|
|
|
|
|
|
|
|
def size(self):
|
|
|
|
return 1
|
|
|
|
|
2018-09-06 17:11:47 +00:00
|
|
|
def serialize(self, addr):
|
|
|
|
return [self.label.serialize(addr)[0]]
|
2015-10-22 15:20:10 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "%s(%r)" % (self.__class__.__name__, self.label)
|
|
|
|
|
|
|
|
|
|
|
|
class LowAddressByte(Emittable):
|
|
|
|
def __init__(self, label):
|
|
|
|
assert isinstance(label, Label)
|
|
|
|
self.label = label
|
|
|
|
|
|
|
|
def size(self):
|
|
|
|
return 1
|
|
|
|
|
2018-09-06 17:11:47 +00:00
|
|
|
def serialize(self, addr):
|
|
|
|
return [self.label.serialize(addr)[1]]
|
2015-10-22 15:20:10 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "%s(%r)" % (self.__class__.__name__, self.label)
|
|
|
|
|
|
|
|
|
|
|
|
# - - - -
|
2015-10-17 14:06:50 +00:00
|
|
|
|
2015-10-16 21:59:35 +00:00
|
|
|
|
|
|
|
class Emitter(object):
|
|
|
|
def __init__(self, addr):
|
|
|
|
self.accum = []
|
2015-10-17 15:51:35 +00:00
|
|
|
self.start_addr = addr
|
2015-10-16 21:59:35 +00:00
|
|
|
self.addr = addr
|
|
|
|
self.name_counter = 0
|
|
|
|
|
2015-10-17 10:08:25 +00:00
|
|
|
def emit(self, *things):
|
2015-10-16 21:59:35 +00:00
|
|
|
for thing in things:
|
2015-10-17 10:08:25 +00:00
|
|
|
self.accum.append(thing)
|
|
|
|
self.addr += thing.size()
|
2015-10-16 21:59:35 +00:00
|
|
|
|
2018-09-06 16:21:43 +00:00
|
|
|
def serialize_to(self, stream):
|
2018-09-06 15:18:41 +00:00
|
|
|
"""`stream` should be a file opened in binary mode."""
|
2015-10-17 15:51:35 +00:00
|
|
|
addr = self.start_addr
|
2015-10-17 10:08:25 +00:00
|
|
|
for emittable in self.accum:
|
2015-10-17 15:51:35 +00:00
|
|
|
chunk = emittable.serialize(addr)
|
2018-09-06 15:18:41 +00:00
|
|
|
stream.write(bytearray(chunk))
|
2015-10-17 15:51:35 +00:00
|
|
|
addr += len(chunk)
|
2015-10-16 21:59:35 +00:00
|
|
|
|
|
|
|
def make_label(self, name=None):
|
|
|
|
if name is None:
|
2015-10-17 09:17:44 +00:00
|
|
|
name = 'label' + str(self.name_counter)
|
2015-10-16 21:59:35 +00:00
|
|
|
self.name_counter += 1
|
|
|
|
return Label(name, addr=self.addr)
|
|
|
|
|
|
|
|
def resolve_label(self, label):
|
|
|
|
label.set_addr(self.addr)
|
2015-10-17 14:46:28 +00:00
|
|
|
|
|
|
|
def resolve_bss_label(self, label):
|
|
|
|
"""Set the given label to be at the current address and
|
|
|
|
advance the address for the next label, but don't emit anything."""
|
|
|
|
self.resolve_label(label)
|
2017-12-11 10:42:42 +00:00
|
|
|
self.addr += label.length
|
2018-03-28 13:20:53 +00:00
|
|
|
|
|
|
|
def size(self):
|
|
|
|
return sum(emittable.size() for emittable in self.accum)
|
|
|
|
|
|
|
|
def pad_to_size(self, size):
|
|
|
|
self_size = self.size()
|
|
|
|
if self_size > size:
|
|
|
|
raise IndexError("Emitter size {} exceeds pad size {}".format(self_size, size))
|
|
|
|
num_bytes = size - self_size
|
|
|
|
if num_bytes > 0:
|
|
|
|
self.accum.extend([Byte(0)] * num_bytes)
|