#! /bin/bash # vim: set tabstop=4 shiftwidth=4 noexpandtab filetype=sh: # mkpo - create ProDOS disk image # # To the extent possible under law, T. Joseph Carter and Ivan Drucker have # waived all copyright and related or neighboring rights to the a2cloud # scripts themselves. Software used or installed by these scripts is subject # to other licenses. This work is published from the United States. # ID-bashByter routines function binToDec () { dec=0; bits=$1; while (( ${#bits} < 8 )); do bits="0$bits"; done; for n in {0..7}; do (( dec+=( ${bits:$n:1} * ( 2**(7-$n) ) ) )); done; echo -n $dec }; function writecharDec () { [[ -n $1 ]] || return 11; [[ -n $2 ]] || return 12; [[ -n $3 ]] || return 13; [[ -n $4 ]] && return 8; [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) && ( $2 -ge 0 ) ]] || return 22; [[ ( $(printf %d "$3" 2> /dev/null) == $3 ) && ( $3 -ge 0 ) && ( $3 -lt 255 ) ]] || return 23; echo -n -e "\x$(printf %02X "$3")" | dd of="$1" bs=1 seek=$(($2)) conv=notrunc 2> /dev/null }; function writecharsHex () { [[ -n $1 ]] || return 11; [[ -n $2 ]] || return 12; [[ -n $3 ]] || return 13; [[ -n $4 ]] && return 8; [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) && ( $2 -ge 0 ) ]] || return 22; p=0; offset=$2; len=${#3}; while (( p < len )); do outByte=${3:$p:2}; [[ $(printf %02X "0x$outByte" 2> /dev/null) == $(echo -n "$outByte" | tr [a-z] [A-Z]) ]] || return 23; echo -n -e "\x$outByte" | dd of="$1" bs=1 seek=$offset conv=notrunc 2> /dev/null; (( p += 3 )); (( offset++ )); done }; # mkpo [[ ! -n $1 ]] && { echo "Usage: mkpo [-b totalBlocks] newImageName [PRODOS.VOL.NAME]"; exit 1; }; [[ -f /usr/local/adtpro/adtpro.sh ]] && adtPath="/usr/local/adtpro" || adtPath=$(ls -1d /Applications/ADTPro* | head -1); [[ ! -d "$adtPath" ]] && { echo "AppleCommander not found."; exit 1; } if [[ $1 == "-b" ]]; then totalBlocks="$2" shift shift fi [[ -f $1 ]] && { echo "Image '$1' already exists."; exit 1; } [[ $2 ]] && prodosVolName="$2" || prodosVolName="UNTITLED" # test ProDOS name legitimacy prodosVolName=$(tr [:lower:] [:upper:] <<< $prodosVolName ) if [[ ${#prodosVolName} -gt 15 || ! $(grep ^[A-Z][0-9A-Z\.]*$ <<< $prodosVolName) ]]; then echo "Invalid ProDOS name: $prodosVolName"; exit 1; fi # see if nulib2 is available; if so, acmd -convert will create image # with specified block size nulib2 &> /dev/null [[ $? == 2 ]] && nulib2=1 || nulib2= if [[ $nulib2 ]]; then if [[ $totalBlocks ]]; then imageBlocks="$totalBlocks" else if [[ $(tr [:upper:] [:lower:] <<< "${1##*.}") == "dsk" ]]; then imageBlocks=280 else imageBlocks=1600 fi fi rm /tmp/blank.shk &> /dev/null orig_dir="$PWD" cd /tmp rm blank.shk EMPTY &> /dev/null touch EMPTY nulib2 -a blank.shk EMPTY &> /dev/null cd "$orig_dir" acmd -convert /tmp/blank.shk "$1" $imageBlocks acmd -d "$1" EMPTY rm /tmp/blank.shk /tmp/EMPTY acmd -n "$1" "$prodosVolName" else # make the disk image without converting archive if [[ $totalBlocks || $(tr [:upper:] [:lower:] <<< "${1##*.}") == "dsk" ]]; then acmd -pro140 "$1" $prodosVolName; else acmd -pro800 "$1" $prodosVolName; fi fi # make the disk bootable if [ -f "$adtPath"/disks/ADTPRO*PO ]; then dd bs=512 count=1 of="$1" conv=notrunc < "$adtPath"/disks/ADTPRO*PO 2> /dev/null fi # change .DSK to DOS-ordered if [[ ! $totalBlocks && $(tr [:upper:] [:lower:] <<< "${1##*.}") == "dsk" ]]; then mv "$1" "$1".tmp for t in {0..34}; do for s in 0 14 13 12 11 10 9 8 7 6 5 4 3 2 1 15; do dd bs=256 count=1 if="$1".tmp of="$1" skip=$(( $t*16 + $s )) seek=$(( $t*16 + ( $s==0||$s==15 ? $s : 15-$s ) )) 2> /dev/null; done; done rm "$1".tmp fi # if nulib2 isn't available, patch the disk image to use specified block size if [[ ! $nulib2 && $totalBlocks ]]; then # change total block count bcHex=$(printf "%04X" $totalBlocks); writecharsHex "$1" 1065 "${bcHex:2:2}.${bcHex:0:2}"; # fix FSB dd if=/dev/zero of="$1" bs=512 seek=280 count=$(( $totalBlocks - 280 )) 2> /dev/null; dd if="$1" of="$1" bs=1 skip=3073 seek=3107 count=$(( ($totalBlocks / 8) - 35 )) conv=notrunc 2> /dev/null; bits=$(( $totalBlocks % 8 )); if (( bits > 0 )); then usedString="00000000"; freeString=; for ((b=0; b<$bits; b++)) do freeString=$freeString"1"; done; binString=$freeString${usedString:$bits}; writecharDec "$1" $(( ( ($totalBlocks / 8) - 35) + 3107 )) $(binToDec $binString); fi; # assign extra blocks to FSB if needed fsbExtraBlocks=$(( ($totalBlocks-1)/4096 )); if (( fsbExtraBlocks > 0 )); then dd if=/dev/zero of="$1" bs=1 seek=3072 count=$(( (fsbExtraBlocks > 8) + 1 )) conv=notrunc 2> /dev/null; (( fsbExtraBlocks-- )); fi; bits=$(( fsbExtraBlocks % 8 )); if (( bits > 0 )); then freeString="11111111"; usedString=; for ((b=0; b<$bits; b++)) do usedString=$usedString"0"; done; binString=$usedString${freeString:$bits}; writecharDec "$1" $(( (fsbExtraBlocks>7)+3073 )) $(binToDec $binString); fi; fi