mirror of
https://github.com/robmcmullen/atrcopy.git
synced 2024-11-25 16:32:07 +00:00
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}")
|
||||
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
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
101
atrcopy/executables.py
Normal 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
|
Loading…
Reference in New Issue
Block a user