mirror of
https://github.com/robmcmullen/atrcopy.git
synced 2024-12-02 00:51:41 +00:00
Added Apple BSAVE file parser
* added object file parsing into assembled files * updated docs
This commit is contained in:
parent
e91c14d1f9
commit
e06a82e611
76
README.rst
76
README.rst
@ -7,6 +7,11 @@ images.
|
|||||||
Prerequisites
|
Prerequisites
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
``atrcopy`` 4.0 introduces a new command line argument structure based on
|
||||||
|
subcommands, much like ``git`` uses ``git pull``, ``git clone``, ``git
|
||||||
|
branch``, etc. Upgrading to 4.0 will require modification of scripts that use
|
||||||
|
``atrcopy`` 3.x-style command line arguments.
|
||||||
|
|
||||||
Starting with ``atrcopy`` 2.0, `numpy <http://www.numpy.org/>`_ is required. It
|
Starting with ``atrcopy`` 2.0, `numpy <http://www.numpy.org/>`_ is required. It
|
||||||
will be automatically installed when installing ``atrcopy`` with::
|
will be automatically installed when installing ``atrcopy`` with::
|
||||||
|
|
||||||
@ -118,7 +123,8 @@ abbreviated as shown here::
|
|||||||
happened
|
happened
|
||||||
|
|
||||||
|
|
||||||
Help for available options for each command is available using::
|
Help for available options for each command is available without specifying a
|
||||||
|
disk image, using a command line like::
|
||||||
|
|
||||||
atrcopy COMMAND --help
|
atrcopy COMMAND --help
|
||||||
|
|
||||||
@ -178,9 +184,10 @@ Extract all files::
|
|||||||
extracting File #4 (.2.u.*) 162 COPY32 COM 056 -> COPY32.COM
|
extracting File #4 (.2.u.*) 162 COPY32 COM 056 -> COPY32.COM
|
||||||
extracting File #5 (.2.u.*) 218 DISKFIX COM 057 -> DISKFIX.COM
|
extracting File #5 (.2.u.*) 218 DISKFIX COM 057 -> DISKFIX.COM
|
||||||
|
|
||||||
Extract all, renaming to lower case on the host file system::
|
Extract all, using the abbreviated commandn and renaming to lower case on the
|
||||||
|
host file system::
|
||||||
|
|
||||||
$ atrcopy DOS_25.ATR extract --all -l
|
$ atrcopy DOS_25.ATR x --all -l
|
||||||
DOS_25.ATR: ATR Disk Image (size=133120 (1040x128b), crc=0 flags=0 unused=0) Atari DOS Format: 1010 usable sectors (739 free), 6 files
|
DOS_25.ATR: ATR Disk Image (size=133120 (1040x128b), crc=0 flags=0 unused=0) Atari DOS Format: 1010 usable sectors (739 free), 6 files
|
||||||
extracting File #0 (.2.u.*) 004 DOS SYS 037 -> dos.sys
|
extracting File #0 (.2.u.*) 004 DOS SYS 037 -> dos.sys
|
||||||
extracting File #1 (.2.u.*) 041 DUP SYS 042 -> dup.sys
|
extracting File #1 (.2.u.*) 041 DUP SYS 042 -> dup.sys
|
||||||
@ -242,31 +249,6 @@ The simple assembler included in ``atrcopy`` can create binary programs by
|
|||||||
connecting binary data together in a single file and specifying a start address
|
connecting binary data together in a single file and specifying a start address
|
||||||
so it can be executed by the system's binary run command.
|
so it can be executed by the system's binary run command.
|
||||||
|
|
||||||
The following command links together a hires image loaded at 2000 hex (the
|
|
||||||
first hires screen) and code at 6000 hex (that was assembled using an external
|
|
||||||
program, in this case the assembler from the cc65 project) and sets a start
|
|
||||||
address of 6000 hex. (Note that all the addresses are implicitly hex values.)
|
|
||||||
Because the Apple ][ binary format is limited to a single contiguous block of
|
|
||||||
data with a start address of the first byte of data loaded, ``atrcopy`` will fill
|
|
||||||
the gaps between any segments that aren't contiguous with zeros. If the start
|
|
||||||
address is not the first byte of the first specified segment, a mini-segment
|
|
||||||
will be included at the beginning that jumps to the specified ``brun`` address
|
|
||||||
(shown here as the segment from 1ffd - 2000). Note the gap between 4000 and
|
|
||||||
6000 hex will be filled with zeros::
|
|
||||||
|
|
||||||
$ atrcopy game.dsk create dos33autobrun
|
|
||||||
using dos33autobrun template: Apple ][ DOS 3.3 (140K) standard RWTS, HGR, BRUN a file named AUTOBRUN
|
|
||||||
created game.dsk: DOS 3.3 Disk Image (size=143360 (560x256b)
|
|
||||||
File #0 ( A) 002 HELLO 003 001
|
|
||||||
|
|
||||||
$ atrcopy game.dsk asm -b title.bin@2000 game[4:]@6000 --brun 6000 -f -o AUTOBRUN
|
|
||||||
game.dsk: DOS 3.3 Disk Image (size=143360 (560x256b)
|
|
||||||
setting data for $1ffd - $2000 at index $0004
|
|
||||||
setting data for $2000 - $4000 at index $0007
|
|
||||||
setting data for $6000 - $6ef3 at index $4007
|
|
||||||
total file size: $4efa (20218) bytes
|
|
||||||
copying AUTOBRUN to game.dsk
|
|
||||||
|
|
||||||
It is also possible to assemble text files that use the MAC/65 syntax, because
|
It is also possible to assemble text files that use the MAC/65 syntax, because
|
||||||
support for `pyatasm <https://pypi.python.org/pypi/pyatasm>`_ is built-in (but
|
support for `pyatasm <https://pypi.python.org/pypi/pyatasm>`_ is built-in (but
|
||||||
optional). MAC/65 is a macro assembler originally designed for the Atari 8-bit
|
optional). MAC/65 is a macro assembler originally designed for the Atari 8-bit
|
||||||
@ -274,6 +256,44 @@ machines but since it produces 6502 code it can be used to compile for any
|
|||||||
machine that uses the 6502: Apple, Commodore, etc.
|
machine that uses the 6502: Apple, Commodore, etc.
|
||||||
|
|
||||||
|
|
||||||
|
Creating DOS 3.3 Binaries
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
For this example, the goal is to produce a single binary file that combines a
|
||||||
|
hi-res image ``title.bin`` loaded at 2000 hex (the first hi-res screen) and
|
||||||
|
code at 6000 hex from the binary file ``game``, with a start address of 6000
|
||||||
|
hex.
|
||||||
|
|
||||||
|
The binary file ``game`` was assembled using the assembler from the
|
||||||
|
`cc65<https://github.com/cc65/cc65>`_ project, using the command::
|
||||||
|
|
||||||
|
cl65 -t apple2 --cpu 6502 --start-addr 0x6000 -o game game.s
|
||||||
|
|
||||||
|
Because the Apple ][ binary format is limited to a single contiguous block of
|
||||||
|
data with a start address of the first byte of data loaded, ``atrcopy`` will
|
||||||
|
fill the gaps between any segments that aren't contiguous with zeros. If the
|
||||||
|
start address is not the first byte of the first specified segment, a small
|
||||||
|
segment will be included at the beginning that jumps to the specified ``brun``
|
||||||
|
address (shown here as the segment from 1ffd - 2000). Note the gap between 4000
|
||||||
|
and 6000 hex will be filled with zeros::
|
||||||
|
|
||||||
|
$ atrcopy game.dsk create dos33autobrun
|
||||||
|
using dos33autobrun template:
|
||||||
|
Apple ][ DOS 3.3 (140K) disk image for binary program development: HELLO sets
|
||||||
|
fullscreen HGR and calls BRUN on user-supplied AUTOBRUN binary file
|
||||||
|
created game.dsk: DOS 3.3 Disk Image (size=143360 (560x256b)
|
||||||
|
File #0 ( A) 002 HELLO 003 001
|
||||||
|
|
||||||
|
$ atrcopy game.dsk asm -d title.bin@2000 -b game --brun 6000 -f -o AUTOBRUN
|
||||||
|
game.dsk: DOS 3.3 Disk Image (size=143360 (560x256b)
|
||||||
|
adding BSAVE data $6000-$6ef3 ($0ef3 @ $0004) from game
|
||||||
|
setting data for $1ffd - $2000 at index $0004
|
||||||
|
setting data for $2000 - $4000 at index $0007
|
||||||
|
setting data for $6000 - $6ef3 at index $4007
|
||||||
|
total file size: $4efa (20218) bytes
|
||||||
|
copying AUTOBRUN to game.dsk
|
||||||
|
|
||||||
|
|
||||||
Example on Mac OS X
|
Example on Mac OS X
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ def crc_files(image, files):
|
|||||||
print("%s: %08x" % (dirent.filename, crc))
|
print("%s: %08x" % (dirent.filename, crc))
|
||||||
|
|
||||||
|
|
||||||
def assemble(image, source_files, data_files, run_addr=""):
|
def assemble(image, source_files, data_files, obj_files, run_addr=""):
|
||||||
if source_files:
|
if source_files:
|
||||||
try:
|
try:
|
||||||
import pyatasm
|
import pyatasm
|
||||||
@ -218,6 +218,13 @@ def assemble(image, source_files, data_files, run_addr=""):
|
|||||||
data = fh.read()[subset]
|
data = fh.read()[subset]
|
||||||
s = segments.add_segment(data, first)
|
s = segments.add_segment(data, first)
|
||||||
log.debug("read data for %s" % s.name)
|
log.debug("read data for %s" % s.name)
|
||||||
|
for name in obj_files:
|
||||||
|
parser = find_diskimage(name)
|
||||||
|
if parser and parser.image:
|
||||||
|
for s in parser.segments:
|
||||||
|
if s.start_addr > 0:
|
||||||
|
print "adding %s from %s" % (s, name)
|
||||||
|
segments.add_segment(s.data, s.start_addr)
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
for s in segments:
|
for s in segments:
|
||||||
print "%s - %04x)" % (str(s)[:-1], s.start_addr + len(s))
|
print "%s - %04x)" % (str(s)[:-1], s.start_addr + len(s))
|
||||||
@ -418,7 +425,8 @@ def run():
|
|||||||
assembly_parser = subparsers.add_parser(command, help="Create a new binary file in the disk image", aliases=command_aliases[command])
|
assembly_parser = subparsers.add_parser(command, help="Create a new binary file in the disk image", aliases=command_aliases[command])
|
||||||
assembly_parser.add_argument("-f", "--force", action="store_true", default=False, help="allow file overwrites in the disk image")
|
assembly_parser.add_argument("-f", "--force", action="store_true", default=False, help="allow file overwrites in the disk image")
|
||||||
assembly_parser.add_argument("-s", "--asm", nargs="*", action="append", help="source file(s) to assemble using pyatasm")
|
assembly_parser.add_argument("-s", "--asm", nargs="*", action="append", help="source file(s) to assemble using pyatasm")
|
||||||
assembly_parser.add_argument("-d","-b", "--data", nargs="*", action="append", help="binary data file(s) to add to assembly, specify as file@addr. Only a portion of the file may be included; specify the subset using standard python slice notation: file[subset]@addr")
|
assembly_parser.add_argument("-d","--data", nargs="*", action="append", help="binary data file(s) to add to assembly, specify as file@addr. Only a portion of the file may be included; specify the subset using standard python slice notation: file[subset]@addr")
|
||||||
|
assembly_parser.add_argument("-b", "--obj", "--bload", nargs="*", action="append", help="binary file(s) to add to assembly, either executables or labeled memory dumps (e.g. BSAVE on Apple ][), parsing each file's binary segments to add to the resulting disk image at the load address for each segment")
|
||||||
assembly_parser.add_argument("-r", "--run-addr", "--brun", action="store", default="", help="run address of binary file if not the first byte of the first segment")
|
assembly_parser.add_argument("-r", "--run-addr", "--brun", action="store", default="", help="run address of binary file if not the first byte of the first segment")
|
||||||
assembly_parser.add_argument("-o", "--output", action="store", default="", required=True, help="output file name in disk image")
|
assembly_parser.add_argument("-o", "--output", action="store", default="", required=True, help="output file name in disk image")
|
||||||
|
|
||||||
@ -522,7 +530,8 @@ def run():
|
|||||||
elif command == "assemble":
|
elif command == "assemble":
|
||||||
asm = options.asm[0] if options.asm else []
|
asm = options.asm[0] if options.asm else []
|
||||||
data = options.data[0] if options.data else []
|
data = options.data[0] if options.data else []
|
||||||
assemble(parser.image, asm, data, options.run_addr)
|
obj = options.obj[0] if options.obj else []
|
||||||
|
assemble(parser.image, asm, data, obj, options.run_addr)
|
||||||
elif command == "segments":
|
elif command == "segments":
|
||||||
print "\n".join([str(a) for a in parser.segments])
|
print "\n".join([str(a) for a in parser.segments])
|
||||||
else:
|
else:
|
||||||
|
@ -625,6 +625,48 @@ class Dos33DiskImage(DiskImageBase):
|
|||||||
return image, 'B'
|
return image, 'B'
|
||||||
|
|
||||||
|
|
||||||
|
class Dos33BinFile(object):
|
||||||
|
"""Parse a binary chunk into segments according to the DOS 3.3 binary
|
||||||
|
dump format
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, rawdata):
|
||||||
|
self.rawdata = rawdata
|
||||||
|
self.size = len(rawdata)
|
||||||
|
self.segments = []
|
||||||
|
self.files = []
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "\n".join(str(s) for s in self.segments) + "\n"
|
||||||
|
|
||||||
|
def strict_check(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def relaxed_check(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def parse_segments(self):
|
||||||
|
r = self.rawdata
|
||||||
|
b = r.get_data()
|
||||||
|
s = r.get_style()
|
||||||
|
pos = 0
|
||||||
|
style_pos = 0
|
||||||
|
first = True
|
||||||
|
log.debug("Initial parsing: size=%d" % self.size)
|
||||||
|
if len(b[pos:pos + 4]) == 4:
|
||||||
|
start, count = b[pos:pos + 4].view(dtype='<u2')
|
||||||
|
s[pos:pos + 4] = get_style_bits(data=True)
|
||||||
|
data = b[pos + 4:pos + 4 + count]
|
||||||
|
if len(data) == count:
|
||||||
|
name = "BSAVE data" % start
|
||||||
|
else:
|
||||||
|
name = "Incomplete data: expected %04x, loaded %04x" % (count, len(data))
|
||||||
|
self.segments.append(ObjSegment(r[pos + 4:pos + 4 + count], pos, pos + 4, start, start + len(data), name))
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.segments.append(ObjSegment(r[pos:pos + 4], 0, 0, 0, len(b[pos:pos + 4]), "Short Segment Header"))
|
||||||
|
|
||||||
|
|
||||||
class ProdosHeader(Dos33Header):
|
class ProdosHeader(Dos33Header):
|
||||||
file_format = "ProDOS"
|
file_format = "ProDOS"
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from ataridos import AtariDosDiskImage, BootDiskImage, AtariDosFile, XexContaine
|
|||||||
from spartados import SpartaDosDiskImage
|
from spartados import SpartaDosDiskImage
|
||||||
from cartridge import AtariCartImage, get_known_carts
|
from cartridge import AtariCartImage, get_known_carts
|
||||||
from mame import MameZipImage
|
from mame import MameZipImage
|
||||||
from dos33 import Dos33DiskImage, ProdosDiskImage
|
from dos33 import Dos33DiskImage, ProdosDiskImage, Dos33BinFile
|
||||||
from errors import *
|
from errors import *
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -130,6 +130,11 @@ class Dos33SegmentParser(SegmentParser):
|
|||||||
image_type = Dos33DiskImage
|
image_type = Dos33DiskImage
|
||||||
|
|
||||||
|
|
||||||
|
class Dos33BinSegmentParser(SegmentParser):
|
||||||
|
menu_name = "BIN (Apple ][ executable)"
|
||||||
|
image_type = Dos33BinFile
|
||||||
|
|
||||||
|
|
||||||
class ProdosSegmentParser(SegmentParser):
|
class ProdosSegmentParser(SegmentParser):
|
||||||
menu_name = "ProDOS Disk Image"
|
menu_name = "ProDOS Disk Image"
|
||||||
image_type = ProdosDiskImage
|
image_type = ProdosDiskImage
|
||||||
@ -187,6 +192,9 @@ mime_parsers = {
|
|||||||
Dos33SegmentParser,
|
Dos33SegmentParser,
|
||||||
ProdosSegmentParser,
|
ProdosSegmentParser,
|
||||||
],
|
],
|
||||||
|
"application/vnd.apple2.bin": [
|
||||||
|
Dos33BinSegmentParser,
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
mime_parse_order = [
|
mime_parse_order = [
|
||||||
@ -196,6 +204,7 @@ mime_parse_order = [
|
|||||||
"application/vnd.atari8bit.5200_cart",
|
"application/vnd.atari8bit.5200_cart",
|
||||||
"application/vnd.mame_rom",
|
"application/vnd.mame_rom",
|
||||||
"application/vnd.apple2.dsk",
|
"application/vnd.apple2.dsk",
|
||||||
|
"application/vnd.apple2.bin",
|
||||||
]
|
]
|
||||||
|
|
||||||
pretty_mime = {
|
pretty_mime = {
|
||||||
@ -205,6 +214,7 @@ pretty_mime = {
|
|||||||
"application/vnd.atari8bit.5200_cart":"Atari 5200 Cartridge",
|
"application/vnd.atari8bit.5200_cart":"Atari 5200 Cartridge",
|
||||||
"application/vnd.mame_rom": "MAME",
|
"application/vnd.mame_rom": "MAME",
|
||||||
"application/vnd.apple2.dsk": "Apple ][ Disk Image",
|
"application/vnd.apple2.dsk": "Apple ][ Disk Image",
|
||||||
|
"application/vnd.apple2.bin": "Apple ][ Binary",
|
||||||
}
|
}
|
||||||
|
|
||||||
grouped_carts = get_known_carts()
|
grouped_carts = get_known_carts()
|
||||||
|
Loading…
Reference in New Issue
Block a user