#!/usr/bin/perl
#
# Copyright © 2015-2020 by Vincent Slyngstad
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE
# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the names of the authors above
# shall not be used in advertising or otherwise to promote the sale, use
# or other dealings in this Software without prior written authorization
# from those authors.
#
# Convert a text version of a .SV file into a .BN file
# First, get the header information.
# JSW bits:
# 0-No 00000-01777
# 1-No 10000-11777
# 2-Reload
# 3-Less 8k
# 10-No pre CD
# 11-No pre USR
@todo = @ARGV;
foreach $f (@todo) {
open(INPUT, $f) || die "$f: $!";
$cs = $sf = $sa = $jsw = undef;
@co = @pc = @fld = ();
while () {
last if /^Block/;
$cs = oct($1) if /^Number of core segments: (\d+)/;
$sf = oct($1) if /^Starting field\s+:\s+(\d+)/;
$sa = oct($1) if /^Starting address:\s+(\d+)/;
$jsw = oct($1) if /^Job status word\s+:\s+(\d+)/;
push(@co, oct($1)) if /^Core origin\s+:\s+(\d+)/;
push(@pc, oct($1)) if /^Pages to load\s+:\s+(\d+)/;
push(@fld, oct($1)) if /Field to load:\s+(\d+)/;
}
$cs--;
die "origin count $#co != $cs\n" unless $#co == $cs;
die "page count $#pc != $cs\n" unless $#pc == $cs;
die "field count $#fld != $cs\n" unless $#fld == $cs;
# At this point, we have parsed a seemingly valid header.
# We have also read the first "Block" line.
die "$f: no Block line" unless /^Block:\s+(\d+)/;
$blk = oct($1);
#
# First, enumerate where each page is to load.
$p = 0;
for ($i = 0; $i <= $cs; $i++) {
$base = $fld[$i]*010000 + $co[$i];
for ($j = 0; $j < $pc[$i]; $j++) {
$pagebase[$p++] = $base + $j*0200;
}
$p++ if $pc[$i] & 0001;
}
undef @core;
while () {
# The lines we care about have an octal offset, colon, then 16 values.
if (s/^(\d+): //) {
$offset = oct($1);
die "Wrong block: $1 $offset($blk)" unless int($offset / 0400) == $blk;
# From the offset in the file, determine where the data will load.
$p = int($offset / 0200); # 128 word page in .SV file.
# Some stuff in a .SV file in the last half a block is never loaded.
next unless defined $pagebase[$p];
$addr = $pagebase[$p] + ($offset % 0200);
# Now store 16/020 values.
for ($i = 0; $i < 020; $i++) {
if (s/^(\d+) +//) {
$core[$addr++] = oct($1);
} else {
die "Invalid octal data ($offset): $_";
}
}
}
# Also keep an eye out for "Block:" lines.
$blk = oct($1) if /^Block:\s+(\d+)/;
}
# At this point we have "loaded" the file.
# Now to dump it back out in the correct format.
# The checksum is simply the 12-bit sum of all bytes
# except field settings and leader-trailer.
# $oaddr = -2;
# for ($addr = 0; $addr <= 077777; $addr++) {
# $oaddr = -2 if ($addr & 007) == 0;
# if (defined $core[$addr]) {
# printf "\n%05o)", $addr unless $addr == $oaddr;
# printf "%04o ", $core[$addr];
# $oaddr = $addr+1;
# }
# }
$of = $f; $of =~ y/A-Z/a-z/;
$of =~ s/\.htm$//;
$of =~ s/\.sv/.bn/;
if (-f $of) {
warn "Skipping $of: exists\n";
next;
}
open(OUTPUT, ">$of") || die "$of: $!";
binmode(OUTPUT);
print "Writing $of\n";
# Leader
for ($i = 0; $i < 64; $i++) {
print OUTPUT "\200";
}
$oaddr = -2;
$cksum = 0;
for ($addr = 0; $addr <= 077777; $addr++) {
if (defined $core[$addr]) {
# Change fields of $oadder is different field from $addr
if (($oaddr & 070000) != ($addr & 070000)) {
print OUTPUT pack("C", 0300 + (($addr >> 9) & 070));
}
# Output an origin if needed.
if ($addr != $oaddr + 1) {
$c1 = 0100 + (($addr >> 6) & 077);
$c2 = $addr & 077;
print OUTPUT pack("CC", $c1, $c2);
$cksum += $c1 + $c2;
}
# Output the data.
$c1 = ($core[$addr] >> 6) & 077;
$c2 = $core[$addr] & 077;
print OUTPUT pack("CC", $c1, $c2);
$cksum += $c1 + $c2;
# Remember the address.
$oaddr = $addr;
}
}
# Output the 12 bit checksum.
$c1 = ($cksum >> 6) & 077;
$c2 = $cksum & 077;
print OUTPUT pack("CC", $c1, $c2);
# Trailer
for ($i = 0; $i < 64; $i++) {
print OUTPUT "\200";
}
close(OUTPUT) || die "$of: $!";
}
exit 0;