Added RunAddressSegment that will supply a run addr to assemble
* moved executable file creation into executables.py (XEX, BSAVE, etc.)
This commit is contained in:
parent
5f5e911d10
commit
54b7d56085
|
@ -228,8 +228,13 @@ def assemble_segments(source_files, data_files, obj_files, run_addr=""):
|
||||||
print(f"skipping {name}: {e}")
|
print(f"skipping {name}: {e}")
|
||||||
else:
|
else:
|
||||||
for s in parser.segments:
|
for s in parser.segments:
|
||||||
if s.origin > 0:
|
if hasattr(s, 'run_address'):
|
||||||
print("adding %s from %s" % (s, name))
|
if not run_addr:
|
||||||
|
run_addr = s.run_address()
|
||||||
|
else:
|
||||||
|
print(f"already have run address {run_addr}; skipping {s.run_address()}")
|
||||||
|
elif s.origin > 0:
|
||||||
|
print(f"adding {s} from {name}")
|
||||||
segments.add_segment(s.data, s.origin)
|
segments.add_segment(s.data, s.origin)
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
for s in segments:
|
for s in segments:
|
||||||
|
@ -237,8 +242,12 @@ def assemble_segments(source_files, data_files, obj_files, run_addr=""):
|
||||||
if run_addr:
|
if run_addr:
|
||||||
try:
|
try:
|
||||||
run_addr = text_to_int(run_addr)
|
run_addr = text_to_int(run_addr)
|
||||||
except ValueError:
|
except (AttributeError, ValueError):
|
||||||
run_addr = None
|
# not text, try as integer
|
||||||
|
try:
|
||||||
|
run_addr = int(run_addr)
|
||||||
|
except ValueError:
|
||||||
|
run_addr = None
|
||||||
|
|
||||||
return segments, run_addr
|
return segments, run_addr
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from . import errors
|
||||||
from .diskimages import DiskImageBase, BaseHeader
|
from .diskimages import DiskImageBase, BaseHeader
|
||||||
from .segments import SegmentData, EmptySegment, ObjSegment, RawSectorsSegment, DefaultSegment, SegmentedFileSegment, SegmentSaver, get_style_bits
|
from .segments import SegmentData, EmptySegment, ObjSegment, RawSectorsSegment, DefaultSegment, SegmentedFileSegment, SegmentSaver, get_style_bits
|
||||||
from .utils import *
|
from .utils import *
|
||||||
|
from .executables import get_xex
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -256,6 +257,16 @@ class XexSegment(ObjSegment):
|
||||||
savers = [SegmentSaver, XexSegmentSaver]
|
savers = [SegmentSaver, XexSegmentSaver]
|
||||||
|
|
||||||
|
|
||||||
|
class RunAddressSegment(ObjSegment):
|
||||||
|
# FIXME: defining run_address as a property doesn't work for some reason.
|
||||||
|
# @property
|
||||||
|
# def run_address(self):
|
||||||
|
# return self.rawdata[0:2].view(dtype="<u2")[0]
|
||||||
|
def run_address(self):
|
||||||
|
return self.rawdata[0:2].data.view(dtype="<u2")[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AtariDosFile:
|
class AtariDosFile:
|
||||||
"""Parse a binary chunk into segments according to the Atari DOS object
|
"""Parse a binary chunk into segments according to the Atari DOS object
|
||||||
file format.
|
file format.
|
||||||
|
@ -313,7 +324,12 @@ class AtariDosFile:
|
||||||
if found < count:
|
if found < count:
|
||||||
self.segments.append(ObjSegment(r[pos + 4:pos + 4 + count], pos, pos + 4, start, end, "Incomplete Data"))
|
self.segments.append(ObjSegment(r[pos + 4:pos + 4 + count], pos, pos + 4, start, end, "Incomplete Data"))
|
||||||
break
|
break
|
||||||
self.segments.append(ObjSegment(r[pos + 4:pos + 4 + count], pos, pos + 4, start, end))
|
if start == 0x2e0:
|
||||||
|
segment_cls = RunAddressSegment
|
||||||
|
else:
|
||||||
|
segment_cls = ObjSegment
|
||||||
|
print(start, end, segment_cls)
|
||||||
|
self.segments.append(segment_cls(r[pos + 4:pos + 4 + count], pos, pos + 4, start, end))
|
||||||
pos += 4 + count
|
pos += 4 + count
|
||||||
style_pos = pos
|
style_pos = pos
|
||||||
|
|
||||||
|
@ -744,56 +760,6 @@ class AtariDiskImage(BootDiskImage):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def get_xex(segments, run_addr=None):
|
|
||||||
segments_copy = [s for s in segments] # don't affect the original list!
|
|
||||||
main_segment = None
|
|
||||||
sub_segments = []
|
|
||||||
data_style = get_style_bits(data=True)
|
|
||||||
total = 2
|
|
||||||
runad = False
|
|
||||||
for s in segments:
|
|
||||||
total += 4 + len(s)
|
|
||||||
if s.origin == 0x2e0:
|
|
||||||
runad = True
|
|
||||||
if not runad:
|
|
||||||
words = np.empty([1], dtype='<u2')
|
|
||||||
if run_addr:
|
|
||||||
found = False
|
|
||||||
for s in segments:
|
|
||||||
if run_addr >= s.origin and run_addr < s.origin + len(s):
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
raise errors.InvalidBinaryFile("Run address points outside data segments")
|
|
||||||
else:
|
|
||||||
run_addr = segments[0].origin
|
|
||||||
words[0] = run_addr
|
|
||||||
r = SegmentData(words.view(dtype=np.uint8))
|
|
||||||
s = DefaultSegment(r, 0x2e0)
|
|
||||||
segments_copy[0:0] = [s]
|
|
||||||
total += 6
|
|
||||||
bytes = np.zeros([total], dtype=np.uint8)
|
|
||||||
rawdata = SegmentData(bytes)
|
|
||||||
main_segment = DefaultSegment(rawdata)
|
|
||||||
main_segment.data[0:2] = 0xff # FFFF header
|
|
||||||
main_segment.style[0:2] = data_style
|
|
||||||
i = 2
|
|
||||||
for s in segments_copy:
|
|
||||||
# create new sub-segment inside new main segment that duplicates the
|
|
||||||
# original segment's data/style
|
|
||||||
new_s = DefaultSegment(rawdata[i:i+4+len(s)], s.origin)
|
|
||||||
words = new_s.data[0:4].view(dtype='<u2')
|
|
||||||
words[0] = s.origin
|
|
||||||
words[1] = s.origin + len(s) - 1
|
|
||||||
new_s.style[0:4] = data_style
|
|
||||||
new_s.data[4:4+len(s)] = s[:]
|
|
||||||
new_s.style[4:4+len(s)] = s.style[:]
|
|
||||||
i += 4 + len(s)
|
|
||||||
new_s.copy_user_data(s, 4)
|
|
||||||
sub_segments.append(new_s)
|
|
||||||
return main_segment, sub_segments
|
|
||||||
|
|
||||||
|
|
||||||
def add_atr_header(bytes):
|
def add_atr_header(bytes):
|
||||||
header = AtrHeader(create=True)
|
header = AtrHeader(create=True)
|
||||||
header.check_size(len(bytes))
|
header.check_size(len(bytes))
|
||||||
|
|
|
@ -4,6 +4,7 @@ from . import errors
|
||||||
from .diskimages import BaseHeader, DiskImageBase
|
from .diskimages import BaseHeader, DiskImageBase
|
||||||
from .utils import Directory, VTOC, WriteableSector, BaseSectorList, Dirent
|
from .utils import Directory, VTOC, WriteableSector, BaseSectorList, Dirent
|
||||||
from .segments import DefaultSegment, EmptySegment, ObjSegment, RawTrackSectorSegment, SegmentSaver, get_style_bits, SegmentData
|
from .segments import DefaultSegment, EmptySegment, ObjSegment, RawTrackSectorSegment, SegmentSaver, get_style_bits, SegmentData
|
||||||
|
from .executables import get_bsave
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -591,42 +592,8 @@ class Dos33DiskImage(DiskImageBase):
|
||||||
return segment
|
return segment
|
||||||
|
|
||||||
def create_executable_file_image(self, segments, run_addr=None):
|
def create_executable_file_image(self, segments, run_addr=None):
|
||||||
# Apple 2 executables get executed at the first address loaded. If the
|
data = get_bsave(segments, run_addr)
|
||||||
# run_addr is not the first byte of the combined data, have to create a
|
return data, 'B'
|
||||||
# new 3-byte segment with a "JMP run_addr" to go at the beginning
|
|
||||||
origin = 100000000
|
|
||||||
last = -1
|
|
||||||
|
|
||||||
for s in segments:
|
|
||||||
origin = min(origin, s.origin)
|
|
||||||
last = max(last, s.origin + len(s))
|
|
||||||
if _xd: log.debug("contiguous bytes needed: %04x - %04x" % (origin, last))
|
|
||||||
if run_addr and run_addr != origin:
|
|
||||||
# check if run_addr points to some location that has data
|
|
||||||
found = False
|
|
||||||
for s in segments:
|
|
||||||
if run_addr >= s.origin and run_addr < s.origin + len(s):
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
raise errors.InvalidBinaryFile("Run address points outside data segments")
|
|
||||||
origin -= 3
|
|
||||||
hi, lo = divmod(run_addr, 256)
|
|
||||||
raw = SegmentData([0x4c, lo, hi])
|
|
||||||
all_segments = [DefaultSegment(raw, origin=origin)]
|
|
||||||
all_segments.extend(segments)
|
|
||||||
else:
|
|
||||||
all_segments = segments
|
|
||||||
size = last - origin
|
|
||||||
image = np.zeros([size + 4], dtype=np.uint8)
|
|
||||||
words = image[0:4].view(dtype="<u2") # always little endian
|
|
||||||
words[0] = origin
|
|
||||||
words[1] = size
|
|
||||||
for s in all_segments:
|
|
||||||
index = s.origin - origin + 4
|
|
||||||
print("setting data for $%04x - $%04x at index $%04x" % (s.origin, s.origin + len(s), index))
|
|
||||||
image[index:index + len(s)] = s.data
|
|
||||||
return image, 'B'
|
|
||||||
|
|
||||||
|
|
||||||
class Dos33BinFile:
|
class Dos33BinFile:
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from . import errors
|
||||||
|
from .diskimages import DiskImageBase, BaseHeader
|
||||||
|
from .segments import SegmentData, EmptySegment, ObjSegment, RawSectorsSegment, DefaultSegment, SegmentedFileSegment, SegmentSaver, get_style_bits
|
||||||
|
from .utils import *
|
||||||
|
|
||||||
|
import logging
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
try: # Expensive debugging
|
||||||
|
_xd = _expensive_debugging
|
||||||
|
except NameError:
|
||||||
|
_xd = False
|
||||||
|
|
||||||
|
|
||||||
|
def get_xex(segments, run_addr=None):
|
||||||
|
segments_copy = [s for s in segments] # don't affect the original list!
|
||||||
|
main_segment = None
|
||||||
|
sub_segments = []
|
||||||
|
data_style = get_style_bits(data=True)
|
||||||
|
total = 2
|
||||||
|
runad = False
|
||||||
|
for s in segments:
|
||||||
|
total += 4 + len(s)
|
||||||
|
if s.origin == 0x2e0:
|
||||||
|
runad = True
|
||||||
|
if not runad:
|
||||||
|
words = np.empty([1], dtype='<u2')
|
||||||
|
if run_addr:
|
||||||
|
found = False
|
||||||
|
for s in segments:
|
||||||
|
if run_addr >= s.origin and run_addr < s.origin + len(s):
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
raise errors.InvalidBinaryFile("Run address points outside data segments")
|
||||||
|
else:
|
||||||
|
run_addr = segments[0].origin
|
||||||
|
words[0] = run_addr
|
||||||
|
r = SegmentData(words.view(dtype=np.uint8))
|
||||||
|
s = DefaultSegment(r, 0x2e0)
|
||||||
|
segments_copy[0:0] = [s]
|
||||||
|
total += 6
|
||||||
|
bytes = np.zeros([total], dtype=np.uint8)
|
||||||
|
rawdata = SegmentData(bytes)
|
||||||
|
main_segment = DefaultSegment(rawdata)
|
||||||
|
main_segment.data[0:2] = 0xff # FFFF header
|
||||||
|
main_segment.style[0:2] = data_style
|
||||||
|
i = 2
|
||||||
|
for s in segments_copy:
|
||||||
|
# create new sub-segment inside new main segment that duplicates the
|
||||||
|
# original segment's data/style
|
||||||
|
new_s = DefaultSegment(rawdata[i:i+4+len(s)], s.origin)
|
||||||
|
words = new_s.data[0:4].view(dtype='<u2')
|
||||||
|
words[0] = s.origin
|
||||||
|
words[1] = s.origin + len(s) - 1
|
||||||
|
new_s.style[0:4] = data_style
|
||||||
|
new_s.data[4:4+len(s)] = s[:]
|
||||||
|
new_s.style[4:4+len(s)] = s.style[:]
|
||||||
|
i += 4 + len(s)
|
||||||
|
new_s.copy_user_data(s, 4)
|
||||||
|
sub_segments.append(new_s)
|
||||||
|
return main_segment, sub_segments
|
||||||
|
|
||||||
|
def get_bsave(segments, run_addr=None):
|
||||||
|
# Apple 2 executables get executed at the first address loaded. If the
|
||||||
|
# run_addr is not the first byte of the combined data, have to create a
|
||||||
|
# new 3-byte segment with a "JMP run_addr" to go at the beginning
|
||||||
|
origin = 100000000
|
||||||
|
last = -1
|
||||||
|
|
||||||
|
for s in segments:
|
||||||
|
origin = min(origin, s.origin)
|
||||||
|
last = max(last, s.origin + len(s))
|
||||||
|
if _xd: log.debug("contiguous bytes needed: %04x - %04x" % (origin, last))
|
||||||
|
if run_addr and run_addr != origin:
|
||||||
|
# check if run_addr points to some location that has data
|
||||||
|
found = False
|
||||||
|
for s in segments:
|
||||||
|
if run_addr >= s.origin and run_addr < s.origin + len(s):
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
raise errors.InvalidBinaryFile("Run address points outside data segments")
|
||||||
|
origin -= 3
|
||||||
|
hi, lo = divmod(run_addr, 256)
|
||||||
|
raw = SegmentData([0x4c, lo, hi])
|
||||||
|
all_segments = [DefaultSegment(raw, origin=origin)]
|
||||||
|
all_segments.extend(segments)
|
||||||
|
else:
|
||||||
|
all_segments = segments
|
||||||
|
size = last - origin
|
||||||
|
image = np.zeros([size + 4], dtype=np.uint8)
|
||||||
|
words = image[0:4].view(dtype="<u2") # always little endian
|
||||||
|
words[0] = origin
|
||||||
|
words[1] = size
|
||||||
|
for s in all_segments:
|
||||||
|
index = s.origin - origin + 4
|
||||||
|
print("setting data for $%04x - $%04x at index $%04x" % (s.origin, s.origin + len(s), index))
|
||||||
|
image[index:index + len(s)] = s.data
|
||||||
|
return image
|
Loading…
Reference in New Issue