Skip to content

Examples

Daniel Gordon edited this page Jul 9, 2023 · 18 revisions

Prerequisites

  • UNIX-like shell is assumed except where noted
  • a2kit must be installed and in the path
  • Some examples require Python 3.8 or higher be installed and in the path

Make Bootable ProDOS Disk

This is a shell session to create a ProDOS disk with an Applesoft greeting program. For the moment, suppose that the Applesoft source code startup.bas and the file images for PRODOS and BASIC.SYSTEM are already in the working directory.

# first create the disk
a2kit mkdsk -o prodos -t woz2 -v demo -d demo.woz
# put the system file images on the disk
a2kit put -f prodos -t any -d demo.woz < prodos.json
a2kit put -f basic.system -t any -d demo.woz < basic.system.json
# tokenize and put the greeting program
a2kit tokenize -t atxt -a 2049 < startup.bas | a2kit put -t atok -f startup -d demo.woz

The source code startup.bas can be created in any text editor. Its contents are up to you, but an example might be

10 home
20 print "hello from my new disk"

The file images can be obtained from another disk image containing the actual files. For example, if you already have a bootable disk image legit.woz, you can create the PRODOS file image like this:

cd /path/to/my/file/images
a2kit get -f prodos -t any -d legit.woz > prodos.json

PowerShell Example

Here is a Windows PowerShell script that deploys BASIC and assembly language files to a disk image. This could be used, e.g., to accelerate a workflow where files are updated in a modern editor, and tested in an emulator.

# Windows PowerShell script deploying files to a disk image.
# This would be run from the local project directory.

# Suppose we have a logical hard drive we are mounting in the emulator:
Set-Variable dimg ($env:USERPROFILE + "\OneDrive\path\to\DISKS\emulatorHD.po")
# Setup paths to the files within the disk image:
Set-Variable f1 "path/to/project/my.assembly.s"
Set-Variable f2 "path/to/project/my.basic"
# There is no overwriting in a2kit, so delete first.
# If the files do not exist we can keep going with no harm, despite the error messages.
a2kit delete -d $dimg -f $f1
a2kit delete -d $dimg -f $f2
# The second pipe transports binary, so this will need to be wrapped in a native shell.
# This is easiest if we put the pipelines into some strings.
Set-Variable cmd1 ('a2kit get -f my.assembly.s | a2kit tokenize -t mtxt | a2kit put -d "' + $dimg + '" -f ' + $f1 + ' -t mtok')
Set-Variable cmd2 ('a2kit get -f my.basic.bas | a2kit tokenize -a 2049 -t atxt | a2kit put -d "' + $dimg + '" -f ' + $f2 + ' -t atok')
cmd /c $cmd1
cmd /c $cmd2
# Catalog the project directory so we can see if it looks alright
a2kit catalog -d $dimg -f path/to/project

Python Front End

The following python module is a simple Python front-end. This is used by some of the other examples, see below. Save this as a2kit.py in the directory with whatever script is importing it.

'''Simple example of a Python front-end.
This mirrors the back-end interface by defining functions that take
the back-end's native argument list as a parameter.'''

import subprocess

def cmd(args):
    '''run a2kit subcommand without any piped input or output, subcommand is first arg'''
    compl = subprocess.run(["a2kit"]+args,capture_output=True,text=False)
    if compl.returncode>0:
        print(compl.stderr)
        exit(1)
def get(args):
    '''run a2kit get in a subprocess and return the output'''
    compl = subprocess.run(["a2kit","get"]+args,capture_output=True,text=False)
    if compl.returncode>0:
        print(compl.stderr)
        exit(1)
    return compl.stdout
def pipe(args,pipe_in):
    '''run a2kit with piped input and output, subcommand is first arg'''
    compl = subprocess.run(["a2kit"]+args,input=pipe_in,capture_output=True,text=False)
    if compl.returncode>0:
        print(compl.stderr)
        exit(1)
    return compl.stdout
def put(args,pipe_in):
    '''run a2kit put in a subprocess with the given input'''
    compl = subprocess.run(["a2kit","put"]+args,input=pipe_in,text=False)
    if compl.returncode>0:
        print(compl.stderr)
        exit(1)

Make a Bootable Kaypro CP/M Disk

In this example we use a Python script to create a bootable CP/M disk for a Kaypro 4. To make a bootable CP/M disk, you will generally need to know some details about how the target vendor chose to lay out their format. In the case of the Kaypro 4, the tricky thing is there are reserved blocks in the user area.

'''Script to make a bootable Kaypro DSDD disk.
This illustrates some low level copying.'''

import sys
import a2kit # front end, see above

# Parse command line
if len(sys.argv)!=3:
    print("usage: python "+sys.argv[0]+" <path in> <path out>")
    print("<path in> path to an existing bootable disk image")
    print("<path out> path to the new disk image")
    exit(1)
path_in = sys.argv[1]
path_out = sys.argv[2]

# Make a blank image, this sets up the track layout etc. for a Kaypro DSDD disk
a2kit.cmd(["mkdsk","-o","cpm2","-t","imd","-k","5.25in-kay4","-d",path_out])

# Copy the reserved track - it is on cylinder 0, side 0.
# Kaypro DSDD has sectors 0-9 on side 0, sectors 10-19 on side 1
print("copying reserved track")
for sec in range(0,10):
    chs = "0,0," + str(sec)
    sec_data = a2kit.get(["-f", chs, "-t", "sec", "-d", path_in])
    a2kit.put(["-f", chs, "-t", "sec", "-d", path_out],sec_data)

# This format has a reserved block at block 1.  We need to copy that too.
# (this could also be done by copying cylinder 0, side 1, sectors 14-17)
print("copying reserved block")
block_data = a2kit.get(["-f", "1", "-t", "block", "-d", path_in])
a2kit.put(["-f", "1", "-t", "block", "-d", path_out],block_data)

# Suppose we want ED.COM on this disk.  Copy it over.
# Obviously this means the source disk must have ED.COM.
# Note the use of the `any` type: we are sending a file image through the pipe.
print("copying files we want")
ed_dot_com = a2kit.get(["-f", "ed.com", "-t", "any", "-d", str(path_in)])
a2kit.put(["-f", "ed.com", "-t", "any", "-d", str(path_out)],ed_dot_com)

Search an Image for a Pattern

Here is a simple Python script that locates multiple occurrences of a pattern within a disk image.

'''Script to search any supported disk image for a pattern in the decoded data.
The search is bounded by ranges of cylinders, heads, and sectors.'''

import sys
import a2kit # front end, see above

# Parse command line
if len(sys.argv)!=6:
    print("usage: python "+sys.argv[0]+" <path> <cyl beg>,<cyl end> <head beg>,<head end> <sec beg>,<sec end> <pattern>")
    print("pattern can be comma-delimited decimals or an ASCII string")
    exit(1)
img_path = sys.argv[1]
cyl_rng = range(int(sys.argv[2].split(',')[0]),int(sys.argv[2].split(',')[1]))
head_rng = range(int(sys.argv[3].split(',')[0]),int(sys.argv[3].split(',')[1]))
sec_rng = range(int(sys.argv[4].split(',')[0]),int(sys.argv[4].split(',')[1]))
raw_patt = sys.argv[5]
matches = 0
try:
    search_pattern = list(map(int,raw_patt.split(',')))
except:
    search_pattern = list(map(ord,raw_patt))

# Carry out the search
for cyl in cyl_rng:
    for head in head_rng:
        for sec in sec_rng:
            args = ["-f", str(cyl)+","+str(head)+","+str(sec), "-t", "sec", "-d", str(img_path)]
            dat = a2kit.get(args)

            for i,val in enumerate(list(dat)):
                if search_pattern[matches]==val:
                    matches += 1
                else:
                    matches = 0
                if matches==len(search_pattern):
                    print("match completed at [cyl,head,sec,pos] = ",[cyl,head,sec,hex(i)])
                    matches = 0

Convert Image Type

a2kit has no subcommand for image conversion, but you can easily make a script to do this. The approach is to use a2kit mkdsk followed by low level copying.

'''Script to change a ProDOS ordered DSK into a DOS ordered one.
If the existing DSK is already DOS ordered there is no harm,
but the existing DSK must contain ProDOS as far as its contents,
otherwise the block-wise copying will not work properly.'''

import sys
import a2kit # front end, see above

# Parse command line
if len(sys.argv)!=3:
    print("usage: python "+sys.argv[0]+" <path in> <path out>")
    print("<path in> path to an existing ProDOS ordered DSK")
    print("<path out> path to the new DOS ordered DSK")
    exit(1)
path_in = sys.argv[1]
path_out = sys.argv[2]

# Make a blank DOS ordered DSK image containing ProDOS
# The volume name doesn't matter as it will be overwritten by the copying
a2kit.cmd(["mkdsk","-o","prodos","-t","do","-v","ANYVOL","-d",path_out])

# Copy all blocks (important to use blocks, not sectors, for a reordering conversion)
for block in range(0,280):
    block_data = a2kit.get(["-f", str(block), "-t", "block", "-d", path_in])
    a2kit.put(["-f", str(block), "-t", "block", "-d", path_out],block_data)

Clone this wiki locally