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}")
else:
for s in parser.segments:
if s.origin > 0:
print("adding %s from %s" % (s, name))
if hasattr(s, 'run_address'):
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)
if options.verbose:
for s in segments:
@ -237,8 +242,12 @@ def assemble_segments(source_files, data_files, obj_files, run_addr=""):
if run_addr:
try:
run_addr = text_to_int(run_addr)
except ValueError:
run_addr = None
except (AttributeError, ValueError):
# not text, try as integer
try:
run_addr = int(run_addr)
except ValueError:
run_addr = None
return segments, run_addr

View File

@ -4,6 +4,7 @@ from . import errors
from .diskimages import DiskImageBase, BaseHeader
from .segments import SegmentData, EmptySegment, ObjSegment, RawSectorsSegment, DefaultSegment, SegmentedFileSegment, SegmentSaver, get_style_bits
from .utils import *
from .executables import get_xex
import logging
log = logging.getLogger(__name__)
@ -256,6 +257,16 @@ class XexSegment(ObjSegment):
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:
"""Parse a binary chunk into segments according to the Atari DOS object
file format.
@ -313,7 +324,12 @@ class AtariDosFile:
if found < count:
self.segments.append(ObjSegment(r[pos + 4:pos + 4 + count], pos, pos + 4, start, end, "Incomplete Data"))
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
style_pos = pos
@ -744,56 +760,6 @@ class AtariDiskImage(BootDiskImage):
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):
header = AtrHeader(create=True)
header.check_size(len(bytes))

View File

@ -4,6 +4,7 @@ from . import errors
from .diskimages import BaseHeader, DiskImageBase
from .utils import Directory, VTOC, WriteableSector, BaseSectorList, Dirent
from .segments import DefaultSegment, EmptySegment, ObjSegment, RawTrackSectorSegment, SegmentSaver, get_style_bits, SegmentData
from .executables import get_bsave
import logging
log = logging.getLogger(__name__)
@ -591,42 +592,8 @@ class Dos33DiskImage(DiskImageBase):
return segment
def create_executable_file_image(self, 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, 'B'
data = get_bsave(segments, run_addr)
return data, 'B'
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