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:
Rob McMullen 2018-07-17 18:10:29 -07:00
parent 5f5e911d10
commit 54b7d56085
4 changed files with 134 additions and 91 deletions

View File

@ -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

View File

@ -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))

View File

@ -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:

101
atrcopy/executables.py Normal file
View File

@ -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