From 8aeec20765b98a81e3140478fc4bd4d550cd7e7d Mon Sep 17 00:00:00 2001 From: Don North Date: Sun, 10 Jul 2016 19:36:50 -0700 Subject: [PATCH 01/19] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..d1d2ecd --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# obj2hex +PDP-11 object file translator / linker From 6e4fe4c66e6b2616b9292670bc44c00019a1546c Mon Sep 17 00:00:00 2001 From: Don North Date: Sun, 10 Jul 2016 19:38:33 -0700 Subject: [PATCH 02/19] Initial version --- README.md | 145 ++++++- obj2hex.pl | 1059 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1202 insertions(+), 2 deletions(-) create mode 100644 obj2hex.pl diff --git a/README.md b/README.md index d1d2ecd..b7fccbf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,143 @@ -# obj2hex -PDP-11 object file translator / linker +tu58em is a ... + +If run with no options, it prints a usage screen: + +``` +obj2hex.pl v1.5 by Don North (perl 5.022) +Usage: ./obj2hex.pl [options...] arguments + --help output manpage and exit + --debug enable debug mode + --verbose verbose status reporting + --boot M9312 boot prom + --console M9312 console/diagnostic prom + --binary binary program load image + --ascii ascii m9312 program load image + --bytes=N bytes per block on output + --nocrc inhibit output of CRC-16 in hex format + --logfile=LOGFILE logging message file + --objfile=OBJFILE macro11 object .obj file + --outfile=OUTFILE output .hex/.txt/.bin file +Aborted due to command line errors. +``` + +If run with the --help option it prints a longer manual page: + +``` +NAME + obj2hex.pl - Convert a Macro-11 program image to PROM/load format + +SYNOPSIS + obj2hex.pl [--help] [--debug] [--verbose] [--boot] [--console] [--binary] + [--ascii] [--bytes=N] [--nocrc] [--logfile=LOGFILE] --objfile=OBJFILE + --outfile=BINFILE + +DESCRIPTION + Converts a Macro-11 object file to various output formats, including M9312 + boot and console PROM, straight binary records, ASCII format for M9312 + console load commands, and loadable absolute binary program images (.BIN) + files. + + Currently the program is limited to a single object input file that can be + output in the selected format. Multiple .psect/.asect ops are supported, + as well as all local (non-global) relocation directory entries. Multiple + object files are (not yet) supported. + +OPTIONS + The following options are available: + + --help + Output this manpage and exit the program. + + --debug + Enable debug mode; print input file records as parsed. + + --verbose + Verbose status; output status messages during processing. + + --boot + Generate a hex PROM file image suitable for programming into an M9312 + boot prom (512x4 geometry, only low half used). + + --console + Generate a hex PROM file image suitable for programming into an M9312 + console/diagnostic prom (1024x4 geometry). + + --binary + Generate binary format load records of the program image (paper tape + format) for loading into SIMH or compatible simulators. These files + can also be copied onto XXDP filesystems to generate runnable program + images (used to write custom diaqnostics). + + --ascii + Generate a a sequence of 'L addr' / 'D data' commands for downloading + a program via a terminal emulator thru the M9312 user command + interface. Suitable only for really small test programs. + + Exactly ONE of --boot, --console, --binary, or --ascii must be + specified. + + --bytes=N + For hex format output files, output N bytes per line (default 16). + + --nocrc + For hex format output files, don't automatically stuff the computed + CRC-16 as the last word in the ROM. + + --logfile=FILENAME + Generate debug output into this file. + + --objfile=FILENAME + Input objject file in .obj format. + + --outfile=FILENAME + Output binary file in format selected by user option. + +ERRORS + The following diagnostic error messages can be produced on STDERR. The + meaning should be fairly self explanatory. + + "Aborted due to command line errors" -- bad option or missing file(s) + + "Can't open input file '$file'" -- bad filename or unreadable file + + "Error: Improper object file format (1)" -- valid record must start with + 0x01 + + "Error: Improper object file format (2)" -- second byte must be 0x00 + + "Error: Improper object file format (3)" -- third byte is low byte of + record length + + "Error: Improper object file format (4)" -- fourth byte is high byte of + record length + + "Error: Improper object file format (5)" -- bytes five thru end-1 are data + bytes + + "Error: Improper object file format (6)" -- last byte is checksum + + "Error: Bad checksum exp=0x%02X rcv=0x%02X" -- compare rcv'ed checksum vs + exp'ed checksum + +EXAMPLES + Some examples of common usage: + + obj2hex.pl --help + + obj2hex.pl --verbose --boot --in 23-751A9.obj --out 23-751A9.hex + + obj2hex.pl --verbose --binary --in memtest.obj --out memtest.bin + +AUTHOR + Don North - donorth + +HISTORY + Modification history: + + 2005-05-05 v1.0 donorth - Initial version. + 2016-01-15 v1.1 donorth - Added RLD(IR) processing, moved sub's to end. + 2016-01-18 v1.2 donorth - Added GSD processing, improved debug output. + 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. + 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. + 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. +``` diff --git a/obj2hex.pl b/obj2hex.pl new file mode 100644 index 0000000..7ced521 --- /dev/null +++ b/obj2hex.pl @@ -0,0 +1,1059 @@ +#!/usr/bin/perl -w +#!/usr/local/bin/perl -w + +# Copyright (c) 2005-2016 Don North +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# o Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# o Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# o Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +require 5.008; + +=head1 NAME + +obj2hex.pl - Convert a Macro-11 program image to PROM/load format + +=head1 SYNOPSIS + +obj2hex.pl +S<[--help]> +S<[--debug]> +S<[--verbose]> +S<[--boot]> +S<[--console]> +S<[--binary]> +S<[--ascii]> +S<[--bytes=N]> +S<[--nocrc]> +S<[--logfile=LOGFILE]> +S<--objfile=OBJFILE> +S<--outfile=BINFILE> + +=head1 DESCRIPTION + +Converts a Macro-11 object file to various output formats, +including M9312 boot and console PROM, straight binary records, +ASCII format for M9312 console load commands, and loadable absolute +binary program images (.BIN) files. + +Currently the program is limited to a single object input file that +can be output in the selected format. Multiple .psect/.asect ops are +supported, as well as all local (non-global) relocation directory +entries. Multiple object files are (not yet) supported. + +=head1 OPTIONS + +The following options are available: + +=over + +=item B<--help> + +Output this manpage and exit the program. + +=item B<--debug> + +Enable debug mode; print input file records as parsed. + +=item B<--verbose> + +Verbose status; output status messages during processing. + +=item B<--boot> + +Generate a hex PROM file image suitable for programming into +an M9312 boot prom (512x4 geometry, only low half used). + +=item B<--console> + +Generate a hex PROM file image suitable for programming into +an M9312 console/diagnostic prom (1024x4 geometry). + +=item B<--binary> + +Generate binary format load records of the program image (paper +tape format) for loading into SIMH or compatible simulators. +These files can also be copied onto XXDP filesystems to generate +runnable program images (used to write custom diaqnostics). + +=item B<--ascii> + +Generate a a sequence of 'L addr' / 'D data' commands for downloading +a program via a terminal emulator thru the M9312 user command interface. +Suitable only for really small test programs. + +Exactly ONE of B<--boot>, B<--console>, B<--binary>, or B<--ascii> +must be specified. + +=item B<--bytes=N> + +For hex format output files, output N bytes per line (default 16). + +=item B<--nocrc> + +For hex format output files, don't automatically stuff the computed +CRC-16 as the last word in the ROM. + +=item B<--logfile=FILENAME> + +Generate debug output into this file. + +=item B<--objfile=FILENAME> + +Input objject file in .obj format. + +=item B<--outfile=FILENAME> + +Output binary file in format selected by user option. + +=back + +=head1 ERRORS + +The following diagnostic error messages can be produced on STDERR. +The meaning should be fairly self explanatory. + +C -- bad option or missing file(s) + +C -- bad filename or unreadable file + +C -- valid record must start with 0x01 + +C -- second byte must be 0x00 + +C -- third byte is low byte of record length + +C -- fourth byte is high byte of record length + +C -- bytes five thru end-1 are data bytes + +C -- last byte is checksum + +C -- compare rcv'ed checksum vs exp'ed checksum + +=head1 EXAMPLES + +Some examples of common usage: + + obj2hex.pl --help + + obj2hex.pl --verbose --boot --in 23-751A9.obj --out 23-751A9.hex + + obj2hex.pl --verbose --binary --in memtest.obj --out memtest.bin + +=head1 AUTHOR + +Don North - donorth + +=head1 HISTORY + +Modification history: + + 2005-05-05 v1.0 donorth - Initial version. + 2016-01-15 v1.1 donorth - Added RLD(IR) processing, moved sub's to end. + 2016-01-18 v1.2 donorth - Added GSD processing, improved debug output. + 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. + 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. + 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. + +=cut + +# options +use strict; + +# external standard modules +use Getopt::Long; +use Pod::Text; +use FindBin; +use FileHandle; + +# external local modules search path +BEGIN { unshift(@INC, $FindBin::Bin); + unshift(@INC, $ENV{PERL5LIB}) if defined($ENV{PERL5LIB}); # cygwin bugfix + unshift(@INC, '.'); } + +# external local modules + +# generic defaults +my $VERSION = 'v1.5'; # version of code +my $HELP = 0; # set to 1 for man page output +my $DEBUG = 0; # set to 1 for debug messages +my $VERBOSE = 0; # set to 1 for verbose messages + +# specific defaults +my $crctype = 'CRC-16'; # type of crc calc to do +my $memsize; # number of instruction bytes allowed +my $memfill; # memory fill pattern +my %excaddr; # words to be skipped in rom crc calc +my $rombase; # base address of rom image +my $romsize; # number of rom addresses +my $romfill; # rom fill pattern +my $romtype = 'NONE'; # default rom type +my $bytesper = -1; # bytes per block in output file +my $nocrc = 0; # output CRC16 as last word unless set +my $objfile = undef; # input filename +my $outfile = undef; # output filename +my $logfile = undef; # log filename + +# process command line arguments +my $NOERROR = GetOptions( "help" => \$HELP, + "debug" => \$DEBUG, + "verbose" => \$VERBOSE, + "boot" => sub { $romtype = 'BOOT'; }, + "console" => sub { $romtype = 'DIAG'; }, + "binary" => sub { $romtype = 'BINA'; }, + "ascii" => sub { $romtype = 'ASC9'; }, + "bytes=i" => \$bytesper, + "nocrc" => \$nocrc, + "objfile=s" => \$objfile, + "outfile=s" => \$outfile, + "logfile=s" => \$logfile, + ); + +# init +$VERBOSE = 1 if $DEBUG; # debug implies verbose messages + +# output the documentation +if ($HELP) { + # output a man page if we can + if (ref(Pod::Text->can('new')) eq 'CODE') { + # try the new way if appears to exist + my $parser = Pod::Text->new(sentence=>0, width=>78); + printf STDOUT "\n"; $parser->parse_from_file($0); + } else { + # else must use the old way + printf STDOUT "\n"; Pod::Text::pod2text(-78, $0); + }; + exit(1); +} + +# check for correct arguments present, print usage if errors +unless ($NOERROR + && scalar(@ARGV) == 0 + && defined($objfile) + && defined($outfile) + && $romtype ne 'NONE' + ) { + printf STDERR "obj2hex.pl %s by Don North (perl %g)\n", $VERSION, $]; + print STDERR "Usage: $0 [options...] arguments\n"; + print STDERR <<"EOF"; + --help output manpage and exit + --debug enable debug mode + --verbose verbose status reporting + --boot M9312 boot prom + --console M9312 console/diagnostic prom + --binary binary program load image + --ascii ascii m9312 program load image + --bytes=N bytes per block on output + --nocrc inhibit output of CRC-16 in hex format + --logfile=LOGFILE logging message file + --objfile=OBJFILE macro11 object .obj file + --outfile=OUTFILE output .hex/.txt/.bin file +EOF + # exit if errors... + die "Aborted due to command line errors.\n"; +} + +# setup log file as a file, defaults to STDERR if not supplied +my $LOG = defined($logfile) ? FileHandle->new("> ".$logfile) : FileHandle->new_from_fd(fileno(STDERR),"w"); + +#---------------------------------------------------------------------------------------------------- + +# subroutine prototypes + +sub trim ($); +sub chksum (@); +sub rad2asc (@); +sub crc (%); +sub read_rec ($); + +#---------------------------------------------------------------------------------------------------- + +# fill in the parameters of the device + +if ($romtype eq 'BOOT') { + + # M9312 512x4 boot prom + %excaddr = ( 024=>1, 025=>1 ); # bytes to be skipped in rom crc calc + $memsize = 128; # number of instruction bytes allowed + $memfill = 0x00; # memory fill pattern + $romsize = 512; # number of rom addresses (must be a power of two) + $romfill = 0x00; # rom fill pattern + $rombase = 0173000; # base address of rom + +} elsif ($romtype eq 'DIAG') { + + # M9312 1024x4 diagnostic/console prom + %excaddr = ( ); # bytes to be skipped in rom crc calc + $memsize = 512; # number of instruction bytes allowed + $memfill = 0x00; # memory fill pattern + $romsize = 1024; # number of rom addresses (must be a power of two) + $romfill = 0x00; # rom fill pattern + $rombase = 0165000; # base address of rom + +} elsif ($romtype eq 'BINA' || $romtype eq 'ASC9') { + + # program load image ... 56KB address space maximum + %excaddr = ( ); # bytes to be skipped in rom crc calc + $memsize = 7*8192; # number of instruction bytes allowed + $memfill = 0x00; # memory fill pattern + $romsize = 8*8192; # number of rom addresses (must be a power of two) + $romfill = 0x00; # image fill pattern + $rombase = 0; # base address of binary image + +} else { + + # unknown ROM type code + die "ROM type '$romtype' is not supported!\n"; + +} + +if ($VERBOSE) { + printf $LOG "ROM type is '%s'\n", $romtype; + printf $LOG "ROM space is %d. bytes\n", $memsize; + printf $LOG "ROM length is %d. addresses\n", $romsize; + printf $LOG "ROM base address is 0%06o\n", $rombase; +} + +#---------------------------------------------------------------------------------------------------- + +# read/process the input object file records + +# real pdp11 memory data words in boot prom +my @mem = ((0) x $memsize); + +# min/max address limits in object file +my ($adrmin,$adrmax) = ('',''); + +# state variables in processing object records +my $rommsk = ($romsize-1)>>1; # address bit mask +my $adrmsk = 0xFFFF; # 16b addr mask +my $datmsk = 0xFFFF; # 16b data mask +my $memmsk = 0xFF; # 8b memory data mask + +# open the input .obj file, die if error +my $OBJ = FileHandle->new("< ".$objfile); +die "Error: can't open input object file '$objfile'\n" unless defined $OBJ; + +# databases +my %gblsym = (); +my %psect = (); +my @psect = (); +my %program = (); +my $psectname = '. ABS.'; +my $psectaddr = 0; +my $psectnumb = -1; +my $textaddr = 0; + +# program defaults +$program{START}{VALUE} = 1; +$program{START}{PSECT} = '. ABS.'; + +# now parse all the records +while (my @rec = &read_rec($OBJ)) { + + # type is first byte of record + my $key = $rec[0]; + + if ($key == 001) { # GSD + + # iterate over GSD subrecords + for (my $i = 2; $i < @rec; ) { + # GSD records are fixed 8B length all in the same format + my $nam = &rad2asc(($rec[$i+1]<<8)|($rec[$i+0]<<0), ($rec[$i+3]<<8)|($rec[$i+2]<<0)); + my $flg = $rec[$i+4]; + my $ent = $rec[$i+5]; + my $val = ($rec[$i+7]<<8)|($rec[$i+6]<<0); + my @ent = ('MODULE','CSECT','INTSYM','XFRADR','GBLSYM','PSECT','IDENT','VSECT'); + if ($ent == 3) { + # XFRADR + $program{START}{PSECT} = $nam; + $program{START}{VALUE} = $val; + } elsif ($ent == 4) { + # GBLSYM flags + $gblsym{$nam}{FLG}{$flg&(1<<0) ? "WEA" : "STR"}++; + $gblsym{$nam}{FLG}{$flg&(1<<3) ? "DEF" : "REF"}++; + $gblsym{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; + $gblsym{$nam}{PSECT} = $psectname; + $gblsym{$nam}{VALUE} = $val; + } elsif ($ent == 5) { + # PSECT flags + $psect[++$psectnumb] = $nam; + $psect{$nam}{NUMBER} = $psectnumb; + $psect{$nam}{FLG}{$flg&(1<<0) ? "GBL" : $flg&(1<<6) ? "GBL" : "LCL"}++; + $psect{$nam}{FLG}{$flg&(1<<2) ? "OVR" : "CAT"}++; + $psect{$nam}{FLG}{$flg&(1<<4) ? "R/O" : "R/W"}++; + $psect{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; + $psect{$nam}{FLG}{$flg&(1<<7) ? "D" : "I/D"}++; + if ($psect{$nam}{FLG}{CAT}) { + $psect{$nam}{LENGTH} = $val; + $psect{$nam}{START} = $psectaddr; + $psectname = $nam; + $psectaddr += $val; + } elsif ($psect{$nam}{FLG}{ABS}) { + $psect{$nam}{LENGTH} = $val; + $psect{$nam}{START} = 0; + $psectname = $nam; + } + } + if ($DEBUG) { + printf $LOG "..GSD: type='%-6s'(%03o) name='%s' value=%06o", $ent[$ent], $ent, $nam, $val; + printf $LOG " flags=%s", join(",", sort(keys(%{$gblsym{$nam}{FLG}}))) if $ent == 4; + printf $LOG " flags=%s", join(",", sort(keys(%{$psect{$nam}{FLG}}))) if $ent == 5; + printf $LOG "\n"; + } + $i += 8; + } + + } elsif ($key == 002) { # ENDGSD + + # just say we saw it + printf $LOG "..ENDGSD\n" if $DEBUG; + + $program{END}{ADDRESS} = 0; + foreach my $nam (sort(keys(%psect))) { + my $start = $psect{$nam}{START}; + my $length = $psect{$nam}{LENGTH}; + my $end = $length ? $start + $length - 1 : $start; + $program{END}{ADDRESS} = $end if $end > $program{END}{ADDRESS}; + printf $LOG "..PSECT[%d](%s) START=%06o END=%06o LENGTH=%06o\n", + $psect{$nam}{NUMBER}, $nam, $start, $end, $length if $length && $DEBUG; + } + $program{START}{ADDRESS} = $program{START}{VALUE} + $psect{$program{START}{PSECT}}{START}; + printf $LOG "..PROG(ADDRESS) START=%06o END=%06o\n", + $program{START}{ADDRESS}, $program{END}{ADDRESS} if $DEBUG; + + } elsif ($key == 003) { # TXT + + # process text record + my $off = ($rec[3]<<8)|($rec[2]<<0); + my $len = @rec-4; + my $base = $psect{$psectname}{START}; + my $adr = ($base + $off) & $adrmsk; + foreach my $i (1..$len) { $mem[$adr+$i-1] = $rec[4+$i-1]; } + if ($DEBUG) { + printf $LOG "..TXT OFFSET=%06o LENGTH=%o BASE=%06o PSECTNAME='%s'\n", $off, $len, $base, $psectname; + for (my $i = 0; $i < $len; $i += 2) { + printf $LOG " %06o: ", ($adr+$i)&~1 if $i%8 == 0; + printf $LOG " %03o...", $mem[$adr+$i++] if ($adr+$i)&1; + printf $LOG " %06o", ($mem[$adr+$i+1]<<8)|($mem[$adr+$i+0]<<0) if $i < $len-1; + printf $LOG " ...%03o", $mem[$adr+$i] if $i == $len-1; + printf $LOG "\n" if $i%8 >= 6 && $i < $len-2; + } + printf $LOG "\n"; + } + $adrmin = $adr if $adrmin eq '' || $adr < $adrmin; + $adrmax = $adr+$len-1 if $adrmax eq '' || $adr+$len-1 > $adrmax; + $textaddr = $adr; + + } elsif ($key == 004) { # RLD + + # iterate over RLD subrecords + for (my $i = 2; $i < @rec; ) { + # first byte is entry type and flags + my $ent = $rec[$i+0] & 0x7F; # entry type + my $flg = $rec[$i+0] & 0x80; # modification flag (0=word, 1=byte) + # process an entry + if ($ent == 001) { + # internal relocation ... OK + my $dis = $rec[$i+1]; + my $con = ($rec[$i+3]<<8)|($rec[$i+2]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$psectname}{START} + $con); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(IR): adr=%06o val=%06o ; dis=%06o con=%06o\n", + $adr, $val, $dis, $con if $DEBUG; + $i += 4; + } elsif ($ent == 002) { + # global relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + my $dis = $rec[$i+1]; + my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); + # process + printf $LOG "..RLD(GR): dis=%06o nam='%s'\n", + $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 003) { + # internal displaced ... OK + my $dis = $rec[$i+1]; + my $con = ($rec[$i+3]<<8)|($rec[$i+2]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($con - ($adr+2)); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(ID): adr=%06o val=%06o ; dis=%06o con=%06o\n", + $adr, $val, $dis, $con if $DEBUG; + $i += 4; + } elsif ($ent == 004) { + # global displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + my $dis = $rec[$i+1]; + my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); + # process + printf $LOG "..RLD(GDR): dis=%06o nam='%s'\n", + $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 005) { + # global additive relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + my $dis = $rec[$i+1]; + my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); + my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); + # process + printf $LOG "..RLD(GAR): dis=%06o con=%06o nam='%s'\n", + $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 006) { + # global additive displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + my $dis = $rec[$i+1]; + my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); + my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); + # process + printf $LOG "..RLD(GADR): dis=%06o con=%06o nam='%s'\n", + $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 007) { + # location counter definition ... OK + my $dis = $rec[$i+1]; + my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); + my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); + # process + $psectname = $nam; + $textaddr = $datmsk & ($con); + printf $LOG "..RLD(LCD): adr=%06o ; dis=%06o con=%06o nam='%s'\n", + $textaddr, $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 010) { + # location counter modification ... OK + my $dis = $rec[$i+1]; + my $con = ($rec[$i+3]<<8)|($rec[$i+2]<<0); + # process + $textaddr = $datmsk & ($con); + printf $LOG "..RLD(LCM): adr=%06o ; dis=%06o con=%06o\n", + $textaddr, $dis, $con if $DEBUG; + $i += 4; + } elsif ($ent == 011) { + # program limits ... OK, mostly + my $dis = $rec[$i+1]; + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ( 01000 ); # make this up, no easy way to compute it + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(LIM1): adr=%06o val=%06o ; dis=%06o\n", + $adr, $val, $dis if $DEBUG; + $dis += 2; + $adr += 2; + $val = $datmsk & ($program{END}{ADDRESS}); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(LIM2): adr=%06o val=%06o ; dis=%06o\n", + $adr, $val, $dis if $DEBUG; + $i += 2; + } elsif ($ent == 012) { + # psect relocation ... OK + my $dis = $rec[$i+1]; + my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START}); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", + $adr, $val, $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 014) { + # psect displaced relocation ... OK + my $dis = $rec[$i+1]; + my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START} - ($adr+2)); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PDR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", + $adr, $val, $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 015) { + # psect additive relocation ... OK + my $dis = $rec[$i+1]; + my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); + my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START} + $con); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PAR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", + $adr, $val, $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 016) { + # psect additive displaced relocation ... OK + my $dis = $rec[$i+1]; + my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); + my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START} + $con - ($adr+2)); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PADR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", + $adr, $val, $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 017) { + # complex relocation ... OK + my $dis = $rec[$i+1]; + my $nam = '. ABS.'; + my $con = 0; + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $loc = 0; + my $val = 0; + my $opc = ""; + my @stk = (); + my $dun = 0; + for ($i += 2; !$dun; $i += 1) { + if ($rec[$i] == 000) { + $opc = "NOP"; + } elsif ($rec[$i] == 001) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] + $arg[1]); + $opc = "ADD"; + } elsif ($rec[$i] == 002) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] - $arg[1]); + $opc = "SUB"; + } elsif ($rec[$i] == 003) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] * $arg[1]); + $opc = "MUL"; + } elsif ($rec[$i] == 004) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[1] == 0 ? 0 : int($arg[0] / $arg[1])); + $opc = "DIV"; + } elsif ($rec[$i] == 005) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] & $arg[1]); + $opc = "AND"; + } elsif ($rec[$i] == 006) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] | $arg[1]); + $opc = "IOR"; + } elsif ($rec[$i] == 007) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] ^ $arg[1]); + $opc = "XOR"; + } elsif ($rec[$i] == 010) { + my @arg = splice(@stk,-1,1); + push(@stk, -$arg[0]); + $opc = "NEG"; + } elsif ($rec[$i] == 011) { + my @arg = splice(@stk,-1,1); + push(@stk, ~$arg[0]); + $opc = "COM"; + } elsif ($rec[$i] == 012) { + my @arg = splice(@stk,-1,1); + $val = $arg[0]; + $opc = "STO"; + $dun = 1; + } elsif ($rec[$i] == 013) { + ############## may need tweaking ################ + my @arg = splice(@stk,-1,1); + $val = $arg[0]; + $opc = "STO+DIS"; + $dun = 1; + } elsif ($rec[$i] == 016) { + ############## may need tweaking ################ + $nam = &rad2asc(($rec[$i+2]<<8)|($rec[$i+1]<<0), + ($rec[$i+4]<<8)|($rec[$i+3]<<0)); + $con = $gblsym{$nam}{VALUE}; + push(@stk, $con); + $opc = sprintf("GLB[%s]=(%o)", &trim($nam), $con); + $i += 4; + } elsif ($rec[$i] == 017) { + $nam = $psect[$rec[$i+1]]; + $con = ($rec[$i+3]<<8) | ($rec[$i+2]<<0); + $loc = $psect{$nam}{START} + $con; + push(@stk, $loc); + $opc = sprintf("FET[%s+%o]=(%o)", &trim($nam), $con, $loc); + $i += 3; + } elsif ($rec[$i] == 020) { + $con = ($rec[$i+2]<<8) | ($rec[$i+1]<<0); + push(@stk, $con); + $opc = "CON"; + $i += 2; + } + $stk[-1] = $datmsk & $stk[-1] if @stk; + printf $LOG "....OPC=%-20s STK=(%s)\n", $opc, join(",",map(sprintf("%o",$_),@stk)) if $DEBUG; + } + printf $LOG "..RLD(CMPX): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; + } else { + die sprintf("Error: Unknown RLD entry 0%o (%d)", $ent, $ent); + } + } + + } elsif ($key == 005) { # ISD + + # ignore + printf $LOG "..ISD: ignored\n" if $DEBUG; + + } elsif ($key == 006) { # ENDMOD + + # just say we saw it + printf $LOG "..ENDMOD\n\n\n" if $DEBUG; + + } elsif ($key == 007) { # LIBHDR + + # ignore + printf $LOG "..LIBHDR: ignored\n" if $DEBUG; + + } elsif ($key == 010) { # LIBEND + + # ignore + printf $LOG "..LIBEND: ignored\n" if $DEBUG; + + } else { # unknown + + # invalid record type in the object file + die sprintf("Error: unknown record type 0%o (%d)", $key, $key); + + } + +} + +# done with object file +$OBJ->close; + +#---------------------------------------------------------------------------------------------------- + +# compute CRC if required, copy memory image to output buffer + +my @buf = ($romfill) x $romsize; # physical PROM data bytes, filled background pattern + +# only compute CRC on M9312 ROMs +if ($romtype eq 'BOOT' || $romtype eq 'DIAG') { + + # compute CRC-16 of the prom contents (except exception words) and store at last location + my $crctab = &crc(-name=>$crctype, -new=>1); + my $crc = &crc(-name=>$crctype, -init=>1); + for (my $adr = 0; $adr < $memsize-2; $adr += 1) { + next if exists($excaddr{$adr}); # skip these addresses + $mem[$rombase+$adr] = $memfill unless defined($mem[$rombase+$adr]); + $crc = &crc(-name=>$crctype, -table=>$crctab, -crc=>$crc, -byte=>$mem[$rombase+$adr]); + } + $crc = &crc(-name=>$crctype, -crc=>$crc, -last=>1); + unless ($nocrc) { + # output computed CRC-16 as last word in the ROM file + $mem[$rombase+$memsize-2] = ($crc>>0)&0xFF; + $mem[$rombase+$memsize-1] = ($crc>>8)&0xFF; + } + printf $LOG "ROM %s is %06o (0x%04X)\n", $crctype, ($crc) x 2 if $VERBOSE; + + # process data words to actual PROM byte data + # put 4bit nibble in low 4b of each 8b data byte, zero the upper 4b + # only copy the above instruction portion over + for (my $idx = 0; $idx < $memsize<<1; $idx += 4) { + my $dat = ($mem[$rombase+($idx>>1)+1]<<8) | ($mem[$rombase+($idx>>1)+0]<<0); + $buf[$idx+0] = ($dat&0xE)|(($dat>>8)&0x1); # bits 3 2 1 8 + $buf[$idx+1] = ($dat>>4)&0xF; # bits 7 6 5 4 + $buf[$idx+2] = ((($dat>>8)&0xE)|($dat&0x1))^0xC; # bits ~11 ~10 9 0 + $buf[$idx+3] = (($dat>>12)&0xF)^0x1; # bits 15 14 13 ~12 + } + +} elsif ($romtype eq 'BINA' || $romtype eq 'ASC9') { + + # only copy the above instruction portion over + for (my $adr = 0; $adr < $memsize; $adr += 1) { + $mem[$rombase+$adr] = $memfill unless defined($mem[$rombase+$adr]); + $buf[$adr] = $mem[$rombase+$adr]; + } + +} + +if ($VERBOSE) { + + # print checksum of entire device + my $chksum = 0; map($chksum += $_, @buf); + printf $LOG "ROM checksum is %06o (0x%04X)\n", $chksum, $chksum; + +} + +#---------------------------------------------------------------------------------------------------- + +# output the linked/processed binary file image in the desired format + +my $OUT = FileHandle->new("> ".$outfile); +die "Error: can't open output file '$outfile'\n" unless defined $OUT; + +if ($romtype eq 'BOOT' || $romtype eq 'DIAG') { + + # output the entire PROM buffer as an intel hex file + + $bytesper = 16 if $bytesper <= 0; + + for (my $idx = 0; $idx < $romsize; $idx += $bytesper) { + my $cnt = $idx+$bytesper <= $romsize ? $bytesper : $romsize-$idx; # N bytes or whatever is left + my @dat = @buf[$idx..($idx+$cnt-1)]; # get the data + my $dat = join('', map(sprintf("%02X",$_),@dat)); # map to ascii text + printf $OUT ":%02X%04X%02X%s%02X\n", $cnt, $idx, 0x00, $dat, &chksum($cnt, $idx>>0, $idx>>8, 0x00, @dat); + } + + printf $OUT ":%02X%04X%02X%s%02X\n", 0x00, 0x0000, 0x01, '', &chksum(0x0, 0x0000>>0, 0x0000>>8, 0x01); + +} elsif ($romtype eq 'BINA') { + + # Loader format consists of blocks, optionally preceded, separated, and + # followed by zeroes. Each block consists of: + # + # 001 --- + # 000 | + # lo(length) | + # hi(length) | + # lo(address) > 'length' bytes + # hi(address) | + # databyte1 | + # : | + # databyteN --- + # checksum + # + # If the byte length is exactly six, the block is the last on the tape, and + # there is no checksum. If the origin is not 000001, then the origin is + # the PC at which to start the program. + + $bytesper = 128 if $bytesper <= 0; + + my $start = $program{START}{ADDRESS}; + + sub m ($) { $_[0] & 0xFF; } + + # output the entire PROM buffer as a binary loader file + for (my $idx = $adrmin; $idx < $adrmax+1; $idx += $bytesper) { + my $cnt = $idx+$bytesper <= $adrmax+1 ? $bytesper : $adrmax+1-$idx; # N bytes or whatever is left + my @dat = @buf[$idx..($idx+$cnt-1)]; # get the data + my $len = $cnt+6; + my @rec = (0x01, 0x00, &m($len>>0), &m($len>>8), &m($idx>>0), &m($idx>>8), @dat); + print $OUT pack("C*", @rec, &chksum(@rec)); + } + my @end = (0x01, 0x00, 0x06, 0x00, &m($start>>0), &m($start>>8)); + print $OUT pack("C*", @end, &chksum(@end)); + +} elsif ($romtype eq 'ASC9') { + + # ascii interface to M9312 console emulator + + sub n ($) { $_[0] & 0xFF; } + + # start program load here + printf $OUT "L %o\r\n", $adrmin; + + # output the PROM buffer as an ascii load file + for (my $idx = $adrmin; $idx < $adrmax+1; $idx += 2) { + printf $OUT "D %06o\r\n", (&n($buf[$idx+1])<<8) | &n($buf[$idx+0]); + } + + # start program exec here + printf $OUT "L %o\r\nS\r\n", $adrmin; + +} + +# all done +$OUT->close; + +#---------------------------------------------------------------------------------------------------- + +# really done +$LOG->close; +exit; + +#---------------------------------------------------------------------------------------------------- + +# trim leading/trailing spaces on a string + +sub trim ($) { + + my ($str) = @_; + + $str =~ s/\s+$//; + $str =~ s/^\s+//; + + return $str; +} + +#---------------------------------------------------------------------------------------------------- + +# compute checksum (twos complement of the sum of bytes) + +sub chksum (@) { + + my $sum = 0; + + map($sum += $_, @_); + + return (-$sum) & 0xFF; +} + +#---------------------------------------------------------------------------------------------------- + +# RAD50 to ASCII decode + +sub rad2asc (@) { + + my @str = split(//, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789'); # RAD50 character subset + + my $ascii = ""; + foreach my $rad50 (@_) { + $ascii .= $str[int($rad50/1600)%40] . $str[int($rad50/40)%40] . $str[$rad50%40]; + } + + return $ascii; +} + +#---------------------------------------------------------------------------------------------------- + +# crc computation routine + +sub crc (%) { + + # pass all args by name + my %args = @_; + + # all the crcs we know how to compute + my %crcdat = ( 'CRC-16' => [ 0xA001, 2, 0x0000, 0x0000 ], + 'CRC-32' => [ 0xEDB88320, 4, 0xFFFFFFFF, 0xFFFFFFFF ] ); + + # run next byte thru crc computation, return updated crc + return $args{-table}[($args{-crc}^$args{-byte}) & 0xFF]^($args{-crc}>>8) if exists($args{-byte}); + + # return initial crc value + return $crcdat{$args{-name}}->[2] if exists($args{-init}); + + # return final crc value xored with xorout + return $args{-crc} ^ $crcdat{$args{-name}}->[3] if exists($args{-last}); + + # compute the crc lookup table, return a pointer to it + if (exists($args{-new})) { + my $crctab = []; + my $poly = $crcdat{$args{-name}}->[0]; + foreach my $byte (0..255) { + my $data = $byte; + foreach (1..8) { $data = ($data>>1) ^ ($data&1 ? $poly : 0); } + $$crctab[$byte] = $data; + } + return $crctab; + } +} + +#---------------------------------------------------------------------------------------------------- + +# read a record from the object file + +sub read_rec ($) { + + my ($fh) = @_; + + my ($buf, $cnt, $len, $err) = (0,0,0,0); + my @pre = (); + my @dat = (); + my @suf = (); + + # Object file format consists of blocks, optionally preceded, separated, and + # followed by zeroes. Each block consists of: + # + # 001 --- + # 000 | + # lo(length) | + # hi(length) > 'length' bytes + # databyte1 | + # : | + # databyteN --- + # checksum + # + + # skip over strings of 0x00; exit OK if hit EOF + do { return () unless $cnt = read($fh, $buf, 1); } while (ord($buf) == 0); + + # valid record starts with (1) + $err = 1 unless $cnt == 1 && ord($buf) == 1; + push(@pre, ord($buf)); + + # second byte must be (0) + $cnt = read($fh, $buf, 1); + $err = 2 unless $cnt == 1 && ord($buf) == 0; + push(@pre, ord($buf)); + + # third byte is low byte of record length + $cnt = read($fh, $buf, 1); + $err = 3 unless $cnt == 1; + $len = ord($buf); + push(@pre, ord($buf)); + + # fourth byte is high byte of record length + $cnt = read($fh, $buf, 1); + $err = 4 unless $cnt == 1; + $len += ord($buf)<<8; + push(@pre, ord($buf)); + + # bytes five thru end-1 are data bytes + $cnt = read($fh, $buf, $len-4); + $err = 5 unless $cnt == $len-4 && $len >= 4; + @dat = unpack("C*", $buf); + + # last byte is checksum + $cnt = read($fh, $buf, 1); + $err = 6 unless $cnt == 1; + my $rcv = ord($buf); + push(@suf, ord($buf)); + + # output the record if debugging + if ($DEBUG) { + my $fmt = "%03o"; + my $n = 16; + my $pre = sprintf("RECORD: [%s] ",join(" ",map(sprintf($fmt,$_),@pre))); + printf $LOG "\n\n%s", $pre; + my $k = length($pre); + my @tmp = @dat; + while (@tmp > $n) { + printf $LOG "%s\n%*s", join(" ",map(sprintf($fmt,$_),splice(@tmp,0,$n))), $k, ''; + } + printf $LOG "%s", join(" ",map(sprintf($fmt,$_),@tmp)) if @tmp; + printf $LOG " [%s]\n\n", join(" ",map(sprintf($fmt,$_),@suf)); + } + + # check we have a well formatted record + die sprintf("Error: invalid object file record format (%d)", $err) if $err; + + # compare rcv'ed checksum vs exp'ed checksum + my $exp = &chksum(0x01, $len>>0, $len>>8, @dat); + die sprintf("Error: Bad checksum exp=0x%02X rcv=0x%02X", $exp, $rcv) unless $exp == $rcv; + + # all is well, return the record + return @dat; +} + +#---------------------------------------------------------------------------------------------------- + +# the end From 9ec5623662fb8d5fc58535e414c4c1b0bb47e9d2 Mon Sep 17 00:00:00 2001 From: Don North Date: Sun, 10 Jul 2016 19:41:57 -0700 Subject: [PATCH 03/19] Corrected header --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7fccbf..ecd5e43 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -tu58em is a ... +obj2hex.pl is a PDP-11 object file translator / linker, transforming an .obj file as output from macro11 into an absolute binary load image file (.bin) or other useful formats (.hex). If run with no options, it prints a usage screen: From 872b33f183a123dc2b709016dd0693b8f7a1c9cc Mon Sep 17 00:00:00 2001 From: AK6DN Date: Wed, 5 Apr 2017 15:35:25 -0700 Subject: [PATCH 04/19] Restructure code in preparation for supporting multiple input .obj files and linking. --- README.md | 18 +- obj2hex.pl | 2134 ++++++++++++++++++++++++++-------------------------- 2 files changed, 1085 insertions(+), 1067 deletions(-) diff --git a/README.md b/README.md index ecd5e43..c2614b2 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Usage: ./obj2hex.pl [options...] arguments --bytes=N bytes per block on output --nocrc inhibit output of CRC-16 in hex format --logfile=LOGFILE logging message file - --objfile=OBJFILE macro11 object .obj file --outfile=OUTFILE output .hex/.txt/.bin file + OBJFILE... macro11 object .obj file(s) Aborted due to command line errors. ``` @@ -28,8 +28,8 @@ NAME SYNOPSIS obj2hex.pl [--help] [--debug] [--verbose] [--boot] [--console] [--binary] - [--ascii] [--bytes=N] [--nocrc] [--logfile=LOGFILE] --objfile=OBJFILE - --outfile=BINFILE + [--ascii] [--bytes=N] [--nocrc] [--logfile=LOGFILE] --outfile=BINFILE + OBJFILE... DESCRIPTION Converts a Macro-11 object file to various output formats, including M9312 @@ -86,12 +86,12 @@ OPTIONS --logfile=FILENAME Generate debug output into this file. - --objfile=FILENAME - Input objject file in .obj format. - --outfile=FILENAME Output binary file in format selected by user option. + OBJFILE... + Input object file(s) in .obj format. + ERRORS The following diagnostic error messages can be produced on STDERR. The meaning should be fairly self explanatory. @@ -124,9 +124,9 @@ EXAMPLES obj2hex.pl --help - obj2hex.pl --verbose --boot --in 23-751A9.obj --out 23-751A9.hex + obj2hex.pl --verbose --boot --out 23-751A9.hex 23-751A9.obj - obj2hex.pl --verbose --binary --in memtest.obj --out memtest.bin + obj2hex.pl --verbose --binary --out memtest.bin memtest.obj AUTHOR Don North - donorth @@ -140,4 +140,6 @@ HISTORY 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. + 2017-04-01 v2.0 donorth - Started to add capability to process multiple + input object files ... still a work in progress. ``` diff --git a/obj2hex.pl b/obj2hex.pl index 7ced521..b27a3fc 100644 --- a/obj2hex.pl +++ b/obj2hex.pl @@ -1,1059 +1,1075 @@ -#!/usr/bin/perl -w -#!/usr/local/bin/perl -w - -# Copyright (c) 2005-2016 Don North -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# o Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -require 5.008; - -=head1 NAME - -obj2hex.pl - Convert a Macro-11 program image to PROM/load format - -=head1 SYNOPSIS - -obj2hex.pl -S<[--help]> -S<[--debug]> -S<[--verbose]> -S<[--boot]> -S<[--console]> -S<[--binary]> -S<[--ascii]> -S<[--bytes=N]> -S<[--nocrc]> -S<[--logfile=LOGFILE]> -S<--objfile=OBJFILE> -S<--outfile=BINFILE> - -=head1 DESCRIPTION - -Converts a Macro-11 object file to various output formats, -including M9312 boot and console PROM, straight binary records, -ASCII format for M9312 console load commands, and loadable absolute -binary program images (.BIN) files. - -Currently the program is limited to a single object input file that -can be output in the selected format. Multiple .psect/.asect ops are -supported, as well as all local (non-global) relocation directory -entries. Multiple object files are (not yet) supported. - -=head1 OPTIONS - -The following options are available: - -=over - -=item B<--help> - -Output this manpage and exit the program. - -=item B<--debug> - -Enable debug mode; print input file records as parsed. - -=item B<--verbose> - -Verbose status; output status messages during processing. - -=item B<--boot> - -Generate a hex PROM file image suitable for programming into -an M9312 boot prom (512x4 geometry, only low half used). - -=item B<--console> - -Generate a hex PROM file image suitable for programming into -an M9312 console/diagnostic prom (1024x4 geometry). - -=item B<--binary> - -Generate binary format load records of the program image (paper -tape format) for loading into SIMH or compatible simulators. -These files can also be copied onto XXDP filesystems to generate -runnable program images (used to write custom diaqnostics). - -=item B<--ascii> - -Generate a a sequence of 'L addr' / 'D data' commands for downloading -a program via a terminal emulator thru the M9312 user command interface. -Suitable only for really small test programs. - -Exactly ONE of B<--boot>, B<--console>, B<--binary>, or B<--ascii> -must be specified. - -=item B<--bytes=N> - -For hex format output files, output N bytes per line (default 16). - -=item B<--nocrc> - -For hex format output files, don't automatically stuff the computed -CRC-16 as the last word in the ROM. - -=item B<--logfile=FILENAME> - -Generate debug output into this file. - -=item B<--objfile=FILENAME> - -Input objject file in .obj format. - -=item B<--outfile=FILENAME> - -Output binary file in format selected by user option. - -=back - -=head1 ERRORS - -The following diagnostic error messages can be produced on STDERR. -The meaning should be fairly self explanatory. - -C -- bad option or missing file(s) - -C -- bad filename or unreadable file - -C -- valid record must start with 0x01 - -C -- second byte must be 0x00 - -C -- third byte is low byte of record length - -C -- fourth byte is high byte of record length - -C -- bytes five thru end-1 are data bytes - -C -- last byte is checksum - -C -- compare rcv'ed checksum vs exp'ed checksum - -=head1 EXAMPLES - -Some examples of common usage: - - obj2hex.pl --help - - obj2hex.pl --verbose --boot --in 23-751A9.obj --out 23-751A9.hex - - obj2hex.pl --verbose --binary --in memtest.obj --out memtest.bin - -=head1 AUTHOR - -Don North - donorth - -=head1 HISTORY - -Modification history: - - 2005-05-05 v1.0 donorth - Initial version. - 2016-01-15 v1.1 donorth - Added RLD(IR) processing, moved sub's to end. - 2016-01-18 v1.2 donorth - Added GSD processing, improved debug output. - 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. - 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. - 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. - -=cut - -# options -use strict; - -# external standard modules -use Getopt::Long; -use Pod::Text; -use FindBin; -use FileHandle; - -# external local modules search path -BEGIN { unshift(@INC, $FindBin::Bin); - unshift(@INC, $ENV{PERL5LIB}) if defined($ENV{PERL5LIB}); # cygwin bugfix - unshift(@INC, '.'); } - -# external local modules - -# generic defaults -my $VERSION = 'v1.5'; # version of code -my $HELP = 0; # set to 1 for man page output -my $DEBUG = 0; # set to 1 for debug messages -my $VERBOSE = 0; # set to 1 for verbose messages - -# specific defaults -my $crctype = 'CRC-16'; # type of crc calc to do -my $memsize; # number of instruction bytes allowed -my $memfill; # memory fill pattern -my %excaddr; # words to be skipped in rom crc calc -my $rombase; # base address of rom image -my $romsize; # number of rom addresses -my $romfill; # rom fill pattern -my $romtype = 'NONE'; # default rom type -my $bytesper = -1; # bytes per block in output file -my $nocrc = 0; # output CRC16 as last word unless set -my $objfile = undef; # input filename -my $outfile = undef; # output filename -my $logfile = undef; # log filename - -# process command line arguments -my $NOERROR = GetOptions( "help" => \$HELP, - "debug" => \$DEBUG, - "verbose" => \$VERBOSE, - "boot" => sub { $romtype = 'BOOT'; }, - "console" => sub { $romtype = 'DIAG'; }, - "binary" => sub { $romtype = 'BINA'; }, - "ascii" => sub { $romtype = 'ASC9'; }, - "bytes=i" => \$bytesper, - "nocrc" => \$nocrc, - "objfile=s" => \$objfile, - "outfile=s" => \$outfile, - "logfile=s" => \$logfile, - ); - -# init -$VERBOSE = 1 if $DEBUG; # debug implies verbose messages - -# output the documentation -if ($HELP) { - # output a man page if we can - if (ref(Pod::Text->can('new')) eq 'CODE') { - # try the new way if appears to exist - my $parser = Pod::Text->new(sentence=>0, width=>78); - printf STDOUT "\n"; $parser->parse_from_file($0); - } else { - # else must use the old way - printf STDOUT "\n"; Pod::Text::pod2text(-78, $0); - }; - exit(1); -} - -# check for correct arguments present, print usage if errors -unless ($NOERROR - && scalar(@ARGV) == 0 - && defined($objfile) - && defined($outfile) - && $romtype ne 'NONE' - ) { - printf STDERR "obj2hex.pl %s by Don North (perl %g)\n", $VERSION, $]; - print STDERR "Usage: $0 [options...] arguments\n"; - print STDERR <<"EOF"; - --help output manpage and exit - --debug enable debug mode - --verbose verbose status reporting - --boot M9312 boot prom - --console M9312 console/diagnostic prom - --binary binary program load image - --ascii ascii m9312 program load image - --bytes=N bytes per block on output - --nocrc inhibit output of CRC-16 in hex format - --logfile=LOGFILE logging message file - --objfile=OBJFILE macro11 object .obj file - --outfile=OUTFILE output .hex/.txt/.bin file -EOF - # exit if errors... - die "Aborted due to command line errors.\n"; -} - -# setup log file as a file, defaults to STDERR if not supplied -my $LOG = defined($logfile) ? FileHandle->new("> ".$logfile) : FileHandle->new_from_fd(fileno(STDERR),"w"); - -#---------------------------------------------------------------------------------------------------- - -# subroutine prototypes - -sub trim ($); -sub chksum (@); -sub rad2asc (@); -sub crc (%); -sub read_rec ($); - -#---------------------------------------------------------------------------------------------------- - -# fill in the parameters of the device - -if ($romtype eq 'BOOT') { - - # M9312 512x4 boot prom - %excaddr = ( 024=>1, 025=>1 ); # bytes to be skipped in rom crc calc - $memsize = 128; # number of instruction bytes allowed - $memfill = 0x00; # memory fill pattern - $romsize = 512; # number of rom addresses (must be a power of two) - $romfill = 0x00; # rom fill pattern - $rombase = 0173000; # base address of rom - -} elsif ($romtype eq 'DIAG') { - - # M9312 1024x4 diagnostic/console prom - %excaddr = ( ); # bytes to be skipped in rom crc calc - $memsize = 512; # number of instruction bytes allowed - $memfill = 0x00; # memory fill pattern - $romsize = 1024; # number of rom addresses (must be a power of two) - $romfill = 0x00; # rom fill pattern - $rombase = 0165000; # base address of rom - -} elsif ($romtype eq 'BINA' || $romtype eq 'ASC9') { - - # program load image ... 56KB address space maximum - %excaddr = ( ); # bytes to be skipped in rom crc calc - $memsize = 7*8192; # number of instruction bytes allowed - $memfill = 0x00; # memory fill pattern - $romsize = 8*8192; # number of rom addresses (must be a power of two) - $romfill = 0x00; # image fill pattern - $rombase = 0; # base address of binary image - -} else { - - # unknown ROM type code - die "ROM type '$romtype' is not supported!\n"; - -} - -if ($VERBOSE) { - printf $LOG "ROM type is '%s'\n", $romtype; - printf $LOG "ROM space is %d. bytes\n", $memsize; - printf $LOG "ROM length is %d. addresses\n", $romsize; - printf $LOG "ROM base address is 0%06o\n", $rombase; -} - -#---------------------------------------------------------------------------------------------------- - -# read/process the input object file records - -# real pdp11 memory data words in boot prom -my @mem = ((0) x $memsize); - -# min/max address limits in object file -my ($adrmin,$adrmax) = ('',''); - -# state variables in processing object records -my $rommsk = ($romsize-1)>>1; # address bit mask -my $adrmsk = 0xFFFF; # 16b addr mask -my $datmsk = 0xFFFF; # 16b data mask -my $memmsk = 0xFF; # 8b memory data mask - -# open the input .obj file, die if error -my $OBJ = FileHandle->new("< ".$objfile); -die "Error: can't open input object file '$objfile'\n" unless defined $OBJ; - -# databases -my %gblsym = (); -my %psect = (); -my @psect = (); -my %program = (); -my $psectname = '. ABS.'; -my $psectaddr = 0; -my $psectnumb = -1; -my $textaddr = 0; - -# program defaults -$program{START}{VALUE} = 1; -$program{START}{PSECT} = '. ABS.'; - -# now parse all the records -while (my @rec = &read_rec($OBJ)) { - - # type is first byte of record - my $key = $rec[0]; - - if ($key == 001) { # GSD - - # iterate over GSD subrecords - for (my $i = 2; $i < @rec; ) { - # GSD records are fixed 8B length all in the same format - my $nam = &rad2asc(($rec[$i+1]<<8)|($rec[$i+0]<<0), ($rec[$i+3]<<8)|($rec[$i+2]<<0)); - my $flg = $rec[$i+4]; - my $ent = $rec[$i+5]; - my $val = ($rec[$i+7]<<8)|($rec[$i+6]<<0); - my @ent = ('MODULE','CSECT','INTSYM','XFRADR','GBLSYM','PSECT','IDENT','VSECT'); - if ($ent == 3) { - # XFRADR - $program{START}{PSECT} = $nam; - $program{START}{VALUE} = $val; - } elsif ($ent == 4) { - # GBLSYM flags - $gblsym{$nam}{FLG}{$flg&(1<<0) ? "WEA" : "STR"}++; - $gblsym{$nam}{FLG}{$flg&(1<<3) ? "DEF" : "REF"}++; - $gblsym{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; - $gblsym{$nam}{PSECT} = $psectname; - $gblsym{$nam}{VALUE} = $val; - } elsif ($ent == 5) { - # PSECT flags - $psect[++$psectnumb] = $nam; - $psect{$nam}{NUMBER} = $psectnumb; - $psect{$nam}{FLG}{$flg&(1<<0) ? "GBL" : $flg&(1<<6) ? "GBL" : "LCL"}++; - $psect{$nam}{FLG}{$flg&(1<<2) ? "OVR" : "CAT"}++; - $psect{$nam}{FLG}{$flg&(1<<4) ? "R/O" : "R/W"}++; - $psect{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; - $psect{$nam}{FLG}{$flg&(1<<7) ? "D" : "I/D"}++; - if ($psect{$nam}{FLG}{CAT}) { - $psect{$nam}{LENGTH} = $val; - $psect{$nam}{START} = $psectaddr; - $psectname = $nam; - $psectaddr += $val; - } elsif ($psect{$nam}{FLG}{ABS}) { - $psect{$nam}{LENGTH} = $val; - $psect{$nam}{START} = 0; - $psectname = $nam; - } - } - if ($DEBUG) { - printf $LOG "..GSD: type='%-6s'(%03o) name='%s' value=%06o", $ent[$ent], $ent, $nam, $val; - printf $LOG " flags=%s", join(",", sort(keys(%{$gblsym{$nam}{FLG}}))) if $ent == 4; - printf $LOG " flags=%s", join(",", sort(keys(%{$psect{$nam}{FLG}}))) if $ent == 5; - printf $LOG "\n"; - } - $i += 8; - } - - } elsif ($key == 002) { # ENDGSD - - # just say we saw it - printf $LOG "..ENDGSD\n" if $DEBUG; - - $program{END}{ADDRESS} = 0; - foreach my $nam (sort(keys(%psect))) { - my $start = $psect{$nam}{START}; - my $length = $psect{$nam}{LENGTH}; - my $end = $length ? $start + $length - 1 : $start; - $program{END}{ADDRESS} = $end if $end > $program{END}{ADDRESS}; - printf $LOG "..PSECT[%d](%s) START=%06o END=%06o LENGTH=%06o\n", - $psect{$nam}{NUMBER}, $nam, $start, $end, $length if $length && $DEBUG; - } - $program{START}{ADDRESS} = $program{START}{VALUE} + $psect{$program{START}{PSECT}}{START}; - printf $LOG "..PROG(ADDRESS) START=%06o END=%06o\n", - $program{START}{ADDRESS}, $program{END}{ADDRESS} if $DEBUG; - - } elsif ($key == 003) { # TXT - - # process text record - my $off = ($rec[3]<<8)|($rec[2]<<0); - my $len = @rec-4; - my $base = $psect{$psectname}{START}; - my $adr = ($base + $off) & $adrmsk; - foreach my $i (1..$len) { $mem[$adr+$i-1] = $rec[4+$i-1]; } - if ($DEBUG) { - printf $LOG "..TXT OFFSET=%06o LENGTH=%o BASE=%06o PSECTNAME='%s'\n", $off, $len, $base, $psectname; - for (my $i = 0; $i < $len; $i += 2) { - printf $LOG " %06o: ", ($adr+$i)&~1 if $i%8 == 0; - printf $LOG " %03o...", $mem[$adr+$i++] if ($adr+$i)&1; - printf $LOG " %06o", ($mem[$adr+$i+1]<<8)|($mem[$adr+$i+0]<<0) if $i < $len-1; - printf $LOG " ...%03o", $mem[$adr+$i] if $i == $len-1; - printf $LOG "\n" if $i%8 >= 6 && $i < $len-2; - } - printf $LOG "\n"; - } - $adrmin = $adr if $adrmin eq '' || $adr < $adrmin; - $adrmax = $adr+$len-1 if $adrmax eq '' || $adr+$len-1 > $adrmax; - $textaddr = $adr; - - } elsif ($key == 004) { # RLD - - # iterate over RLD subrecords - for (my $i = 2; $i < @rec; ) { - # first byte is entry type and flags - my $ent = $rec[$i+0] & 0x7F; # entry type - my $flg = $rec[$i+0] & 0x80; # modification flag (0=word, 1=byte) - # process an entry - if ($ent == 001) { - # internal relocation ... OK - my $dis = $rec[$i+1]; - my $con = ($rec[$i+3]<<8)|($rec[$i+2]<<0); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($psect{$psectname}{START} + $con); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(IR): adr=%06o val=%06o ; dis=%06o con=%06o\n", - $adr, $val, $dis, $con if $DEBUG; - $i += 4; - } elsif ($ent == 002) { - # global relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - my $dis = $rec[$i+1]; - my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); - # process - printf $LOG "..RLD(GR): dis=%06o nam='%s'\n", - $dis, $nam if $DEBUG; - $i += 6; - } elsif ($ent == 003) { - # internal displaced ... OK - my $dis = $rec[$i+1]; - my $con = ($rec[$i+3]<<8)|($rec[$i+2]<<0); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($con - ($adr+2)); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(ID): adr=%06o val=%06o ; dis=%06o con=%06o\n", - $adr, $val, $dis, $con if $DEBUG; - $i += 4; - } elsif ($ent == 004) { - # global displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - my $dis = $rec[$i+1]; - my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); - # process - printf $LOG "..RLD(GDR): dis=%06o nam='%s'\n", - $dis, $nam if $DEBUG; - $i += 6; - } elsif ($ent == 005) { - # global additive relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - my $dis = $rec[$i+1]; - my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); - my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); - # process - printf $LOG "..RLD(GAR): dis=%06o con=%06o nam='%s'\n", - $dis, $con, $nam if $DEBUG; - $i += 8; - } elsif ($ent == 006) { - # global additive displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - my $dis = $rec[$i+1]; - my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); - my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); - # process - printf $LOG "..RLD(GADR): dis=%06o con=%06o nam='%s'\n", - $dis, $con, $nam if $DEBUG; - $i += 8; - } elsif ($ent == 007) { - # location counter definition ... OK - my $dis = $rec[$i+1]; - my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); - my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); - # process - $psectname = $nam; - $textaddr = $datmsk & ($con); - printf $LOG "..RLD(LCD): adr=%06o ; dis=%06o con=%06o nam='%s'\n", - $textaddr, $dis, $con, $nam if $DEBUG; - $i += 8; - } elsif ($ent == 010) { - # location counter modification ... OK - my $dis = $rec[$i+1]; - my $con = ($rec[$i+3]<<8)|($rec[$i+2]<<0); - # process - $textaddr = $datmsk & ($con); - printf $LOG "..RLD(LCM): adr=%06o ; dis=%06o con=%06o\n", - $textaddr, $dis, $con if $DEBUG; - $i += 4; - } elsif ($ent == 011) { - # program limits ... OK, mostly - my $dis = $rec[$i+1]; - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ( 01000 ); # make this up, no easy way to compute it - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(LIM1): adr=%06o val=%06o ; dis=%06o\n", - $adr, $val, $dis if $DEBUG; - $dis += 2; - $adr += 2; - $val = $datmsk & ($program{END}{ADDRESS}); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(LIM2): adr=%06o val=%06o ; dis=%06o\n", - $adr, $val, $dis if $DEBUG; - $i += 2; - } elsif ($ent == 012) { - # psect relocation ... OK - my $dis = $rec[$i+1]; - my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($psect{$nam}{START}); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(PR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", - $adr, $val, $dis, $nam if $DEBUG; - $i += 6; - } elsif ($ent == 014) { - # psect displaced relocation ... OK - my $dis = $rec[$i+1]; - my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($psect{$nam}{START} - ($adr+2)); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(PDR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", - $adr, $val, $dis, $nam if $DEBUG; - $i += 6; - } elsif ($ent == 015) { - # psect additive relocation ... OK - my $dis = $rec[$i+1]; - my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); - my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($psect{$nam}{START} + $con); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(PAR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", - $adr, $val, $dis, $con, $nam if $DEBUG; - $i += 8; - } elsif ($ent == 016) { - # psect additive displaced relocation ... OK - my $dis = $rec[$i+1]; - my $nam = &rad2asc(($rec[$i+3]<<8)|($rec[$i+2]<<0), ($rec[$i+5]<<8)|($rec[$i+4]<<0)); - my $con = ($rec[$i+7]<<8)|($rec[$i+6]<<0); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($psect{$nam}{START} + $con - ($adr+2)); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(PADR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", - $adr, $val, $dis, $con, $nam if $DEBUG; - $i += 8; - } elsif ($ent == 017) { - # complex relocation ... OK - my $dis = $rec[$i+1]; - my $nam = '. ABS.'; - my $con = 0; - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $loc = 0; - my $val = 0; - my $opc = ""; - my @stk = (); - my $dun = 0; - for ($i += 2; !$dun; $i += 1) { - if ($rec[$i] == 000) { - $opc = "NOP"; - } elsif ($rec[$i] == 001) { - my @arg = splice(@stk,-2,2); - push(@stk, $arg[0] + $arg[1]); - $opc = "ADD"; - } elsif ($rec[$i] == 002) { - my @arg = splice(@stk,-2,2); - push(@stk, $arg[0] - $arg[1]); - $opc = "SUB"; - } elsif ($rec[$i] == 003) { - my @arg = splice(@stk,-2,2); - push(@stk, $arg[0] * $arg[1]); - $opc = "MUL"; - } elsif ($rec[$i] == 004) { - my @arg = splice(@stk,-2,2); - push(@stk, $arg[1] == 0 ? 0 : int($arg[0] / $arg[1])); - $opc = "DIV"; - } elsif ($rec[$i] == 005) { - my @arg = splice(@stk,-2,2); - push(@stk, $arg[0] & $arg[1]); - $opc = "AND"; - } elsif ($rec[$i] == 006) { - my @arg = splice(@stk,-2,2); - push(@stk, $arg[0] | $arg[1]); - $opc = "IOR"; - } elsif ($rec[$i] == 007) { - my @arg = splice(@stk,-2,2); - push(@stk, $arg[0] ^ $arg[1]); - $opc = "XOR"; - } elsif ($rec[$i] == 010) { - my @arg = splice(@stk,-1,1); - push(@stk, -$arg[0]); - $opc = "NEG"; - } elsif ($rec[$i] == 011) { - my @arg = splice(@stk,-1,1); - push(@stk, ~$arg[0]); - $opc = "COM"; - } elsif ($rec[$i] == 012) { - my @arg = splice(@stk,-1,1); - $val = $arg[0]; - $opc = "STO"; - $dun = 1; - } elsif ($rec[$i] == 013) { - ############## may need tweaking ################ - my @arg = splice(@stk,-1,1); - $val = $arg[0]; - $opc = "STO+DIS"; - $dun = 1; - } elsif ($rec[$i] == 016) { - ############## may need tweaking ################ - $nam = &rad2asc(($rec[$i+2]<<8)|($rec[$i+1]<<0), - ($rec[$i+4]<<8)|($rec[$i+3]<<0)); - $con = $gblsym{$nam}{VALUE}; - push(@stk, $con); - $opc = sprintf("GLB[%s]=(%o)", &trim($nam), $con); - $i += 4; - } elsif ($rec[$i] == 017) { - $nam = $psect[$rec[$i+1]]; - $con = ($rec[$i+3]<<8) | ($rec[$i+2]<<0); - $loc = $psect{$nam}{START} + $con; - push(@stk, $loc); - $opc = sprintf("FET[%s+%o]=(%o)", &trim($nam), $con, $loc); - $i += 3; - } elsif ($rec[$i] == 020) { - $con = ($rec[$i+2]<<8) | ($rec[$i+1]<<0); - push(@stk, $con); - $opc = "CON"; - $i += 2; - } - $stk[-1] = $datmsk & $stk[-1] if @stk; - printf $LOG "....OPC=%-20s STK=(%s)\n", $opc, join(",",map(sprintf("%o",$_),@stk)) if $DEBUG; - } - printf $LOG "..RLD(CMPX): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; - } else { - die sprintf("Error: Unknown RLD entry 0%o (%d)", $ent, $ent); - } - } - - } elsif ($key == 005) { # ISD - - # ignore - printf $LOG "..ISD: ignored\n" if $DEBUG; - - } elsif ($key == 006) { # ENDMOD - - # just say we saw it - printf $LOG "..ENDMOD\n\n\n" if $DEBUG; - - } elsif ($key == 007) { # LIBHDR - - # ignore - printf $LOG "..LIBHDR: ignored\n" if $DEBUG; - - } elsif ($key == 010) { # LIBEND - - # ignore - printf $LOG "..LIBEND: ignored\n" if $DEBUG; - - } else { # unknown - - # invalid record type in the object file - die sprintf("Error: unknown record type 0%o (%d)", $key, $key); - - } - -} - -# done with object file -$OBJ->close; - -#---------------------------------------------------------------------------------------------------- - -# compute CRC if required, copy memory image to output buffer - -my @buf = ($romfill) x $romsize; # physical PROM data bytes, filled background pattern - -# only compute CRC on M9312 ROMs -if ($romtype eq 'BOOT' || $romtype eq 'DIAG') { - - # compute CRC-16 of the prom contents (except exception words) and store at last location - my $crctab = &crc(-name=>$crctype, -new=>1); - my $crc = &crc(-name=>$crctype, -init=>1); - for (my $adr = 0; $adr < $memsize-2; $adr += 1) { - next if exists($excaddr{$adr}); # skip these addresses - $mem[$rombase+$adr] = $memfill unless defined($mem[$rombase+$adr]); - $crc = &crc(-name=>$crctype, -table=>$crctab, -crc=>$crc, -byte=>$mem[$rombase+$adr]); - } - $crc = &crc(-name=>$crctype, -crc=>$crc, -last=>1); - unless ($nocrc) { - # output computed CRC-16 as last word in the ROM file - $mem[$rombase+$memsize-2] = ($crc>>0)&0xFF; - $mem[$rombase+$memsize-1] = ($crc>>8)&0xFF; - } - printf $LOG "ROM %s is %06o (0x%04X)\n", $crctype, ($crc) x 2 if $VERBOSE; - - # process data words to actual PROM byte data - # put 4bit nibble in low 4b of each 8b data byte, zero the upper 4b - # only copy the above instruction portion over - for (my $idx = 0; $idx < $memsize<<1; $idx += 4) { - my $dat = ($mem[$rombase+($idx>>1)+1]<<8) | ($mem[$rombase+($idx>>1)+0]<<0); - $buf[$idx+0] = ($dat&0xE)|(($dat>>8)&0x1); # bits 3 2 1 8 - $buf[$idx+1] = ($dat>>4)&0xF; # bits 7 6 5 4 - $buf[$idx+2] = ((($dat>>8)&0xE)|($dat&0x1))^0xC; # bits ~11 ~10 9 0 - $buf[$idx+3] = (($dat>>12)&0xF)^0x1; # bits 15 14 13 ~12 - } - -} elsif ($romtype eq 'BINA' || $romtype eq 'ASC9') { - - # only copy the above instruction portion over - for (my $adr = 0; $adr < $memsize; $adr += 1) { - $mem[$rombase+$adr] = $memfill unless defined($mem[$rombase+$adr]); - $buf[$adr] = $mem[$rombase+$adr]; - } - -} - -if ($VERBOSE) { - - # print checksum of entire device - my $chksum = 0; map($chksum += $_, @buf); - printf $LOG "ROM checksum is %06o (0x%04X)\n", $chksum, $chksum; - -} - -#---------------------------------------------------------------------------------------------------- - -# output the linked/processed binary file image in the desired format - -my $OUT = FileHandle->new("> ".$outfile); -die "Error: can't open output file '$outfile'\n" unless defined $OUT; - -if ($romtype eq 'BOOT' || $romtype eq 'DIAG') { - - # output the entire PROM buffer as an intel hex file - - $bytesper = 16 if $bytesper <= 0; - - for (my $idx = 0; $idx < $romsize; $idx += $bytesper) { - my $cnt = $idx+$bytesper <= $romsize ? $bytesper : $romsize-$idx; # N bytes or whatever is left - my @dat = @buf[$idx..($idx+$cnt-1)]; # get the data - my $dat = join('', map(sprintf("%02X",$_),@dat)); # map to ascii text - printf $OUT ":%02X%04X%02X%s%02X\n", $cnt, $idx, 0x00, $dat, &chksum($cnt, $idx>>0, $idx>>8, 0x00, @dat); - } - - printf $OUT ":%02X%04X%02X%s%02X\n", 0x00, 0x0000, 0x01, '', &chksum(0x0, 0x0000>>0, 0x0000>>8, 0x01); - -} elsif ($romtype eq 'BINA') { - - # Loader format consists of blocks, optionally preceded, separated, and - # followed by zeroes. Each block consists of: - # - # 001 --- - # 000 | - # lo(length) | - # hi(length) | - # lo(address) > 'length' bytes - # hi(address) | - # databyte1 | - # : | - # databyteN --- - # checksum - # - # If the byte length is exactly six, the block is the last on the tape, and - # there is no checksum. If the origin is not 000001, then the origin is - # the PC at which to start the program. - - $bytesper = 128 if $bytesper <= 0; - - my $start = $program{START}{ADDRESS}; - - sub m ($) { $_[0] & 0xFF; } - - # output the entire PROM buffer as a binary loader file - for (my $idx = $adrmin; $idx < $adrmax+1; $idx += $bytesper) { - my $cnt = $idx+$bytesper <= $adrmax+1 ? $bytesper : $adrmax+1-$idx; # N bytes or whatever is left - my @dat = @buf[$idx..($idx+$cnt-1)]; # get the data - my $len = $cnt+6; - my @rec = (0x01, 0x00, &m($len>>0), &m($len>>8), &m($idx>>0), &m($idx>>8), @dat); - print $OUT pack("C*", @rec, &chksum(@rec)); - } - my @end = (0x01, 0x00, 0x06, 0x00, &m($start>>0), &m($start>>8)); - print $OUT pack("C*", @end, &chksum(@end)); - -} elsif ($romtype eq 'ASC9') { - - # ascii interface to M9312 console emulator - - sub n ($) { $_[0] & 0xFF; } - - # start program load here - printf $OUT "L %o\r\n", $adrmin; - - # output the PROM buffer as an ascii load file - for (my $idx = $adrmin; $idx < $adrmax+1; $idx += 2) { - printf $OUT "D %06o\r\n", (&n($buf[$idx+1])<<8) | &n($buf[$idx+0]); - } - - # start program exec here - printf $OUT "L %o\r\nS\r\n", $adrmin; - -} - -# all done -$OUT->close; - -#---------------------------------------------------------------------------------------------------- - -# really done -$LOG->close; -exit; - -#---------------------------------------------------------------------------------------------------- - -# trim leading/trailing spaces on a string - -sub trim ($) { - - my ($str) = @_; - - $str =~ s/\s+$//; - $str =~ s/^\s+//; - - return $str; -} - -#---------------------------------------------------------------------------------------------------- - -# compute checksum (twos complement of the sum of bytes) - -sub chksum (@) { - - my $sum = 0; - - map($sum += $_, @_); - - return (-$sum) & 0xFF; -} - -#---------------------------------------------------------------------------------------------------- - -# RAD50 to ASCII decode - -sub rad2asc (@) { - - my @str = split(//, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789'); # RAD50 character subset - - my $ascii = ""; - foreach my $rad50 (@_) { - $ascii .= $str[int($rad50/1600)%40] . $str[int($rad50/40)%40] . $str[$rad50%40]; - } - - return $ascii; -} - -#---------------------------------------------------------------------------------------------------- - -# crc computation routine - -sub crc (%) { - - # pass all args by name - my %args = @_; - - # all the crcs we know how to compute - my %crcdat = ( 'CRC-16' => [ 0xA001, 2, 0x0000, 0x0000 ], - 'CRC-32' => [ 0xEDB88320, 4, 0xFFFFFFFF, 0xFFFFFFFF ] ); - - # run next byte thru crc computation, return updated crc - return $args{-table}[($args{-crc}^$args{-byte}) & 0xFF]^($args{-crc}>>8) if exists($args{-byte}); - - # return initial crc value - return $crcdat{$args{-name}}->[2] if exists($args{-init}); - - # return final crc value xored with xorout - return $args{-crc} ^ $crcdat{$args{-name}}->[3] if exists($args{-last}); - - # compute the crc lookup table, return a pointer to it - if (exists($args{-new})) { - my $crctab = []; - my $poly = $crcdat{$args{-name}}->[0]; - foreach my $byte (0..255) { - my $data = $byte; - foreach (1..8) { $data = ($data>>1) ^ ($data&1 ? $poly : 0); } - $$crctab[$byte] = $data; - } - return $crctab; - } -} - -#---------------------------------------------------------------------------------------------------- - -# read a record from the object file - -sub read_rec ($) { - - my ($fh) = @_; - - my ($buf, $cnt, $len, $err) = (0,0,0,0); - my @pre = (); - my @dat = (); - my @suf = (); - - # Object file format consists of blocks, optionally preceded, separated, and - # followed by zeroes. Each block consists of: - # - # 001 --- - # 000 | - # lo(length) | - # hi(length) > 'length' bytes - # databyte1 | - # : | - # databyteN --- - # checksum - # - - # skip over strings of 0x00; exit OK if hit EOF - do { return () unless $cnt = read($fh, $buf, 1); } while (ord($buf) == 0); - - # valid record starts with (1) - $err = 1 unless $cnt == 1 && ord($buf) == 1; - push(@pre, ord($buf)); - - # second byte must be (0) - $cnt = read($fh, $buf, 1); - $err = 2 unless $cnt == 1 && ord($buf) == 0; - push(@pre, ord($buf)); - - # third byte is low byte of record length - $cnt = read($fh, $buf, 1); - $err = 3 unless $cnt == 1; - $len = ord($buf); - push(@pre, ord($buf)); - - # fourth byte is high byte of record length - $cnt = read($fh, $buf, 1); - $err = 4 unless $cnt == 1; - $len += ord($buf)<<8; - push(@pre, ord($buf)); - - # bytes five thru end-1 are data bytes - $cnt = read($fh, $buf, $len-4); - $err = 5 unless $cnt == $len-4 && $len >= 4; - @dat = unpack("C*", $buf); - - # last byte is checksum - $cnt = read($fh, $buf, 1); - $err = 6 unless $cnt == 1; - my $rcv = ord($buf); - push(@suf, ord($buf)); - - # output the record if debugging - if ($DEBUG) { - my $fmt = "%03o"; - my $n = 16; - my $pre = sprintf("RECORD: [%s] ",join(" ",map(sprintf($fmt,$_),@pre))); - printf $LOG "\n\n%s", $pre; - my $k = length($pre); - my @tmp = @dat; - while (@tmp > $n) { - printf $LOG "%s\n%*s", join(" ",map(sprintf($fmt,$_),splice(@tmp,0,$n))), $k, ''; - } - printf $LOG "%s", join(" ",map(sprintf($fmt,$_),@tmp)) if @tmp; - printf $LOG " [%s]\n\n", join(" ",map(sprintf($fmt,$_),@suf)); - } - - # check we have a well formatted record - die sprintf("Error: invalid object file record format (%d)", $err) if $err; - - # compare rcv'ed checksum vs exp'ed checksum - my $exp = &chksum(0x01, $len>>0, $len>>8, @dat); - die sprintf("Error: Bad checksum exp=0x%02X rcv=0x%02X", $exp, $rcv) unless $exp == $rcv; - - # all is well, return the record - return @dat; -} - -#---------------------------------------------------------------------------------------------------- - -# the end +#!/usr/bin/perl -w +#!/usr/local/bin/perl -w + +# Copyright (c) 2005-2016 Don North +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# o Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# o Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# o Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +require 5.008; + +=head1 NAME + +obj2hex.pl - Convert a Macro-11 program image to PROM/load format + +=head1 SYNOPSIS + +obj2hex.pl +S<[--help]> +S<[--debug]> +S<[--verbose]> +S<[--boot]> +S<[--console]> +S<[--binary]> +S<[--ascii]> +S<[--bytes=N]> +S<[--nocrc]> +S<[--logfile=LOGFILE]> +S<--outfile=BINFILE> +S + +=head1 DESCRIPTION + +Converts a Macro-11 object files to various output formats, +including M9312 boot and console PROM, straight binary records, +ASCII format for M9312 console load commands, and loadable absolute +binary program images (.BIN) files. + +Multiple .psect/.asect ops are supported, as well as all local +(non-global) relocation directory entries. + +Multiple input object files are (not yet fully) supported - this +part is work in progress. In particular definition and resolution +of global symbols are not supported. + +=head1 OPTIONS + +The following options are available: + +=over + +=item B<--help> + +Output this manpage and exit the program. + +=item B<--debug> + +Enable debug mode; print input file records as parsed. + +=item B<--verbose> + +Verbose status; output status messages during processing. + +=item B<--boot> + +Generate a hex PROM file image suitable for programming into +an M9312 boot prom (512x4 geometry, only low half used). + +=item B<--console> + +Generate a hex PROM file image suitable for programming into +an M9312 console/diagnostic prom (1024x4 geometry). + +=item B<--binary> + +Generate binary format load records of the program image (paper +tape format) for loading into SIMH or compatible simulators. +These files can also be copied onto XXDP filesystems to generate +runnable program images (used to write custom diaqnostics). + +=item B<--ascii> + +Generate a a sequence of 'L addr' / 'D data' commands for downloading +a program via a terminal emulator thru the M9312 user command interface. +Suitable only for really small test programs. + +Exactly ONE of B<--boot>, B<--console>, B<--binary>, or B<--ascii> +must be specified. + +=item B<--bytes=N> + +For hex format output files, output N bytes per line (default 16). + +=item B<--nocrc> + +For hex format output files, don't automatically stuff the computed +CRC-16 as the last word in the ROM. + +=item B<--logfile=FILENAME> + +Generate debug output into this file. + +=item B<--outfile=FILENAME> + +Output binary file in format selected by user option. + +=item B + +Input object file(s) in .obj format. + +=back + +=head1 ERRORS + +The following diagnostic error messages can be produced on STDERR. +The meaning should be fairly self explanatory. + +C -- bad option or missing file(s) + +C -- bad filename or unreadable file + +C -- valid record must start with 0x01 + +C -- second byte must be 0x00 + +C -- third byte is low byte of record length + +C -- fourth byte is high byte of record length + +C -- bytes five thru end-1 are data bytes + +C -- last byte is checksum + +C -- compare rcv'ed checksum vs exp'ed checksum + +=head1 EXAMPLES + +Some examples of common usage: + + obj2hex.pl --help + + obj2hex.pl --verbose --boot --out 23-751A9.hex 23-751A9.obj + + obj2hex.pl --verbose --binary --out memtest.bin memtest.obj + +=head1 AUTHOR + +Don North - donorth + +=head1 HISTORY + +Modification history: + + 2005-05-05 v1.0 donorth - Initial version. + 2016-01-15 v1.1 donorth - Added RLD(IR) processing, moved sub's to end. + 2016-01-18 v1.2 donorth - Added GSD processing, improved debug output. + 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. + 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. + 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. + 2017-04-01 v2.0 donorth - Started to add capability to process multiple + input object files ... still a work in progress. + +=cut + +# options +use strict; + +# external standard modules +use Getopt::Long; +use Pod::Text; +use FindBin; +use FileHandle; + +# external local modules search path +BEGIN { unshift(@INC, $FindBin::Bin); + unshift(@INC, $ENV{PERL5LIB}) if defined($ENV{PERL5LIB}); # cygwin bugfix + unshift(@INC, '.'); } + +# external local modules + +# generic defaults +my $VERSION = 'v2.0'; # version of code +my $HELP = 0; # set to 1 for man page output +my $DEBUG = 0; # set to 1 for debug messages +my $VERBOSE = 0; # set to 1 for verbose messages + +# specific defaults +my $crctype = 'CRC-16'; # type of crc calc to do +my $memsize; # number of instruction bytes allowed +my $memfill; # memory fill pattern +my %excaddr; # words to be skipped in rom crc calc +my $rombase; # base address of rom image +my $romsize; # number of rom addresses +my $romfill; # rom fill pattern +my $romtype = 'NONE'; # default rom type +my $bytesper = -1; # bytes per block in output file +my $nocrc = 0; # output CRC16 as last word unless set +my $outfile = undef; # output filename +my $logfile = undef; # log filename + +# process command line arguments +my $NOERROR = GetOptions( "help" => \$HELP, + "debug" => \$DEBUG, + "verbose" => \$VERBOSE, + "boot" => sub { $romtype = 'BOOT'; }, + "console" => sub { $romtype = 'DIAG'; }, + "binary" => sub { $romtype = 'BINA'; }, + "ascii" => sub { $romtype = 'ASC9'; }, + "bytes=i" => \$bytesper, + "nocrc" => \$nocrc, + "outfile=s" => \$outfile, + "logfile=s" => \$logfile, + ); + +# init +$VERBOSE = 1 if $DEBUG; # debug implies verbose messages + +# output the documentation +if ($HELP) { + # output a man page if we can + if (ref(Pod::Text->can('new')) eq 'CODE') { + # try the new way if appears to exist + my $parser = Pod::Text->new(sentence=>0, width=>78); + printf STDOUT "\n"; $parser->parse_from_file($0); + } else { + # else must use the old way + printf STDOUT "\n"; Pod::Text::pod2text(-78, $0); + }; + exit(1); +} + +# check for correct arguments present, print usage if errors +unless ($NOERROR + && scalar(@ARGV) >= 1 + && defined($outfile) + && $romtype ne 'NONE' + ) { + printf STDERR "obj2hex.pl %s by Don North (perl %g)\n", $VERSION, $]; + print STDERR "Usage: $0 [options...] arguments\n"; + print STDERR <<"EOF"; + --help output manpage and exit + --debug enable debug mode + --verbose verbose status reporting + --boot M9312 boot prom + --console M9312 console/diagnostic prom + --binary binary program load image + --ascii ascii m9312 program load image + --bytes=N bytes per block on output + --nocrc inhibit output of CRC-16 in hex format + --logfile=LOGFILE logging message file + --outfile=OUTFILE output .hex/.txt/.bin file + OBJFILE... macro11 object .obj file(s) +EOF + # exit if errors... + die "Aborted due to command line errors.\n"; +} + +# setup log file as a file, defaults to STDERR if not supplied +my $LOG = defined($logfile) ? FileHandle->new("> ".$logfile) : FileHandle->new_from_fd(fileno(STDERR),"w"); + +#---------------------------------------------------------------------------------------------------- + +# subroutine prototypes + +sub trim ($); +sub chksum (@); +sub rad2asc (@); +sub crc (%); +sub read_rec ($); +sub parse_rec ($); + +#---------------------------------------------------------------------------------------------------- + +# fill in the parameters of the device + +if ($romtype eq 'BOOT') { + + # M9312 512x4 boot prom + %excaddr = ( 024=>1, 025=>1 ); # bytes to be skipped in rom crc calc + $memsize = 128; # number of instruction bytes allowed + $memfill = 0x00; # memory fill pattern + $romsize = 512; # number of rom addresses (must be a power of two) + $romfill = 0x00; # rom fill pattern + $rombase = 0173000; # base address of rom + +} elsif ($romtype eq 'DIAG') { + + # M9312 1024x4 diagnostic/console prom + %excaddr = ( ); # bytes to be skipped in rom crc calc + $memsize = 512; # number of instruction bytes allowed + $memfill = 0x00; # memory fill pattern + $romsize = 1024; # number of rom addresses (must be a power of two) + $romfill = 0x00; # rom fill pattern + $rombase = 0165000; # base address of rom + +} elsif ($romtype eq 'BINA' || $romtype eq 'ASC9') { + + # program load image ... 56KB address space maximum + %excaddr = ( ); # bytes to be skipped in rom crc calc + $memsize = 7*8192; # number of instruction bytes allowed + $memfill = 0x00; # memory fill pattern + $romsize = 8*8192; # number of rom addresses (must be a power of two) + $romfill = 0x00; # image fill pattern + $rombase = 0; # base address of binary image + +} else { + + # unknown ROM type code + die "ROM type '$romtype' is not supported!\n"; + +} + +if ($VERBOSE) { + printf $LOG "ROM type is '%s'\n", $romtype; + printf $LOG "ROM space is %d. bytes\n", $memsize; + printf $LOG "ROM length is %d. addresses\n", $romsize; + printf $LOG "ROM base address is 0%06o\n", $rombase; +} + +#---------------------------------------------------------------------------------------------------- + +# read/process the input object file records + +# real pdp11 memory data words in boot prom +my @mem = ((0) x $memsize); + +# min/max address limits in object file +my ($adrmin,$adrmax) = ('',''); + +# state variables in processing object records +my $rommsk = ($romsize-1)>>1; # address bit mask +my $adrmsk = 0xFFFF; # 16b addr mask +my $datmsk = 0xFFFF; # 16b data mask +my $memmsk = 0xFF; # 8b memory data mask + +# databases +my %gblsym = (); +my %psect = (); +my @psect = (); +my %program = (); +my $psectname = '. ABS.'; +my $psectaddr = 0; +my $psectnumb = -1; +my $textaddr = 0; + +# program defaults +$program{START}{VALUE} = 1; +$program{START}{PSECT} = '. ABS.'; + +# process all object files +while (my $objfile = shift(@ARGV)) { + + # open the input .obj file, die if error + my $OBJ = FileHandle->new("< ".$objfile); + die "Error: can't open input object file '$objfile'\n" unless defined $OBJ; + + # now parse all the records + while (my @rec = &read_rec($OBJ)) { &parse_rec(\@rec); } + + # done with object file + $OBJ->close; +} + +#---------------------------------------------------------------------------------------------------- + +# compute CRC if required, copy memory image to output buffer + +my @buf = ($romfill) x $romsize; # physical PROM data bytes, filled background pattern + +# only compute CRC on M9312 ROMs +if ($romtype eq 'BOOT' || $romtype eq 'DIAG') { + + # compute CRC-16 of the prom contents (except exception words) and store at last location + my $crctab = &crc(-name=>$crctype, -new=>1); + my $crc = &crc(-name=>$crctype, -init=>1); + for (my $adr = 0; $adr < $memsize-2; $adr += 1) { + next if exists($excaddr{$adr}); # skip these addresses + $mem[$rombase+$adr] = $memfill unless defined($mem[$rombase+$adr]); + $crc = &crc(-name=>$crctype, -table=>$crctab, -crc=>$crc, -byte=>$mem[$rombase+$adr]); + } + $crc = &crc(-name=>$crctype, -crc=>$crc, -last=>1); + unless ($nocrc) { + # output computed CRC-16 as last word in the ROM file + $mem[$rombase+$memsize-2] = ($crc>>0)&0xFF; + $mem[$rombase+$memsize-1] = ($crc>>8)&0xFF; + } + printf $LOG "ROM %s is %06o (0x%04X)\n", $crctype, ($crc) x 2 if $VERBOSE; + + # process data words to actual PROM byte data + # put 4bit nibble in low 4b of each 8b data byte, zero the upper 4b + # only copy the above instruction portion over + for (my $idx = 0; $idx < $memsize<<1; $idx += 4) { + my $dat = ($mem[$rombase+($idx>>1)+1]<<8) | ($mem[$rombase+($idx>>1)+0]<<0); + $buf[$idx+0] = ($dat&0xE)|(($dat>>8)&0x1); # bits 3 2 1 8 + $buf[$idx+1] = ($dat>>4)&0xF; # bits 7 6 5 4 + $buf[$idx+2] = ((($dat>>8)&0xE)|($dat&0x1))^0xC; # bits ~11 ~10 9 0 + $buf[$idx+3] = (($dat>>12)&0xF)^0x1; # bits 15 14 13 ~12 + } + +} elsif ($romtype eq 'BINA' || $romtype eq 'ASC9') { + + # only copy the above instruction portion over + for (my $adr = 0; $adr < $memsize; $adr += 1) { + $mem[$rombase+$adr] = $memfill unless defined($mem[$rombase+$adr]); + $buf[$adr] = $mem[$rombase+$adr]; + } + +} + +if ($VERBOSE) { + + # print checksum of entire device + my $chksum = 0; map($chksum += $_, @buf); + printf $LOG "ROM checksum is %06o (0x%04X)\n", $chksum, $chksum; + +} + +#---------------------------------------------------------------------------------------------------- + +# output the linked/processed binary file image in the desired format + +my $OUT = FileHandle->new("> ".$outfile); +die "Error: can't open output file '$outfile'\n" unless defined $OUT; + +if ($romtype eq 'BOOT' || $romtype eq 'DIAG') { + + # output the entire PROM buffer as an intel hex file + + $bytesper = 16 if $bytesper <= 0; + + for (my $idx = 0; $idx < $romsize; $idx += $bytesper) { + my $cnt = $idx+$bytesper <= $romsize ? $bytesper : $romsize-$idx; # N bytes or whatever is left + my @dat = @buf[$idx..($idx+$cnt-1)]; # get the data + my $dat = join('', map(sprintf("%02X",$_),@dat)); # map to ascii text + printf $OUT ":%02X%04X%02X%s%02X\n", $cnt, $idx, 0x00, $dat, &chksum($cnt, $idx>>0, $idx>>8, 0x00, @dat); + } + + printf $OUT ":%02X%04X%02X%s%02X\n", 0x00, 0x0000, 0x01, '', &chksum(0x0, 0x0000>>0, 0x0000>>8, 0x01); + +} elsif ($romtype eq 'BINA') { + + # Loader format consists of blocks, optionally preceded, separated, and + # followed by zeroes. Each block consists of: + # + # 001 --- + # 000 | + # lo(length) | + # hi(length) | + # lo(address) > 'length' bytes + # hi(address) | + # databyte1 | + # : | + # databyteN --- + # checksum + # + # If the byte length is exactly six, the block is the last on the tape, and + # there is no checksum. If the origin is not 000001, then the origin is + # the PC at which to start the program. + + $bytesper = 128 if $bytesper <= 0; + + my $start = $program{START}{ADDRESS}; + + sub m ($) { $_[0] & 0xFF; } + + # output the entire PROM buffer as a binary loader file + for (my $idx = $adrmin; $idx < $adrmax+1; $idx += $bytesper) { + my $cnt = $idx+$bytesper <= $adrmax+1 ? $bytesper : $adrmax+1-$idx; # N bytes or whatever is left + my @dat = @buf[$idx..($idx+$cnt-1)]; # get the data + my $len = $cnt+6; + my @rec = (0x01, 0x00, &m($len>>0), &m($len>>8), &m($idx>>0), &m($idx>>8), @dat); + print $OUT pack("C*", @rec, &chksum(@rec)); + } + my @end = (0x01, 0x00, 0x06, 0x00, &m($start>>0), &m($start>>8)); + print $OUT pack("C*", @end, &chksum(@end)); + +} elsif ($romtype eq 'ASC9') { + + # ascii interface to M9312 console emulator + + sub n ($) { $_[0] & 0xFF; } + + # start program load here + printf $OUT "L %o\r\n", $adrmin; + + # output the PROM buffer as an ascii load file + for (my $idx = $adrmin; $idx < $adrmax+1; $idx += 2) { + printf $OUT "D %06o\r\n", (&n($buf[$idx+1])<<8) | &n($buf[$idx+0]); + } + + # start program exec here + printf $OUT "L %o\r\nS\r\n", $adrmin; + +} + +# all done +$OUT->close; + +#---------------------------------------------------------------------------------------------------- + +# really done +$LOG->close; +exit; + +#---------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------- + +# trim leading/trailing spaces on a string + +sub trim ($) { + + my ($str) = @_; + + $str =~ s/\s+$//; + $str =~ s/^\s+//; + + return $str; +} + +#---------------------------------------------------------------------------------------------------- + +# compute checksum (twos complement of the sum of bytes) + +sub chksum (@) { + + my $sum = 0; + + map($sum += $_, @_); + + return (-$sum) & 0xFF; +} + +#---------------------------------------------------------------------------------------------------- + +# RAD50 to ASCII decode + +sub rad2asc (@) { + + my @str = split(//, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789'); # RAD50 character subset + + my $ascii = ""; + foreach my $rad50 (@_) { + $ascii .= $str[int($rad50/1600)%40] . $str[int($rad50/40)%40] . $str[$rad50%40]; + } + + return $ascii; +} + +#---------------------------------------------------------------------------------------------------- + +# crc computation routine + +sub crc (%) { + + # pass all args by name + my %args = @_; + + # all the crcs we know how to compute + my %crcdat = ( 'CRC-16' => [ 0xA001, 2, 0x0000, 0x0000 ], + 'CRC-32' => [ 0xEDB88320, 4, 0xFFFFFFFF, 0xFFFFFFFF ] ); + + # run next byte thru crc computation, return updated crc + return $args{-table}[($args{-crc}^$args{-byte}) & 0xFF]^($args{-crc}>>8) if exists($args{-byte}); + + # return initial crc value + return $crcdat{$args{-name}}->[2] if exists($args{-init}); + + # return final crc value xored with xorout + return $args{-crc} ^ $crcdat{$args{-name}}->[3] if exists($args{-last}); + + # compute the crc lookup table, return a pointer to it + if (exists($args{-new})) { + my $crctab = []; + my $poly = $crcdat{$args{-name}}->[0]; + foreach my $byte (0..255) { + my $data = $byte; + foreach (1..8) { $data = ($data>>1) ^ ($data&1 ? $poly : 0); } + $$crctab[$byte] = $data; + } + return $crctab; + } +} + +#---------------------------------------------------------------------------------------------------- + +# read a record from the object file + +sub read_rec ($) { + + my ($fh) = @_; + + my ($buf, $cnt, $len, $err) = (0,0,0,0); + my @pre = (); + my @dat = (); + my @suf = (); + + # Object file format consists of blocks, optionally preceded, separated, and + # followed by zeroes. Each block consists of: + # + # 001 --- + # 000 | + # lo(length) | + # hi(length) > 'length' bytes + # databyte1 | + # : | + # databyteN --- + # checksum + # + + # skip over strings of 0x00; exit OK if hit EOF + do { return () unless $cnt = read($fh, $buf, 1); } while (ord($buf) == 0); + + # valid record starts with (1) + $err = 1 unless $cnt == 1 && ord($buf) == 1; + push(@pre, ord($buf)); + + # second byte must be (0) + $cnt = read($fh, $buf, 1); + $err = 2 unless $cnt == 1 && ord($buf) == 0; + push(@pre, ord($buf)); + + # third byte is low byte of record length + $cnt = read($fh, $buf, 1); + $err = 3 unless $cnt == 1; + $len = ord($buf); + push(@pre, ord($buf)); + + # fourth byte is high byte of record length + $cnt = read($fh, $buf, 1); + $err = 4 unless $cnt == 1; + $len += ord($buf)<<8; + push(@pre, ord($buf)); + + # bytes five thru end-1 are data bytes + $cnt = read($fh, $buf, $len-4); + $err = 5 unless $cnt == $len-4 && $len >= 4; + @dat = unpack("C*", $buf); + + # last byte is checksum + $cnt = read($fh, $buf, 1); + $err = 6 unless $cnt == 1; + my $rcv = ord($buf); + push(@suf, ord($buf)); + + # output the record if debugging + if ($DEBUG) { + my $fmt = "%03o"; + my $n = 16; + my $pre = sprintf("RECORD: [%s] ",join(" ",map(sprintf($fmt,$_),@pre))); + printf $LOG "\n\n%s", $pre; + my $k = length($pre); + my @tmp = @dat; + while (@tmp > $n) { + printf $LOG "%s\n%*s", join(" ",map(sprintf($fmt,$_),splice(@tmp,0,$n))), $k, ''; + } + printf $LOG "%s", join(" ",map(sprintf($fmt,$_),@tmp)) if @tmp; + printf $LOG " [%s]\n\n", join(" ",map(sprintf($fmt,$_),@suf)); + } + + # check we have a well formatted record + die sprintf("Error: invalid object file record format (%d)", $err) if $err; + + # compare rcv'ed checksum vs exp'ed checksum + my $exp = &chksum(0x01, $len>>0, $len>>8, @dat); + die sprintf("Error: Bad checksum exp=0x%02X rcv=0x%02X", $exp, $rcv) unless $exp == $rcv; + + # all is well, return the record + return @dat; +} + +#---------------------------------------------------------------------------------------------------- + +# parse an input object file record, update data structures + +sub parse_rec ($) { + + my ($rec) = (@_); + + # type is first byte of record + my $key = $rec->[0]; + + if ($key == 001) { # GSD + + # iterate over GSD subrecords + for (my $i = 2; $i < scalar(@$rec); ) { + # GSD records are fixed 8B length all in the same format + my $nam = &rad2asc(($rec->[$i+1]<<8)|($rec->[$i+0]<<0), ($rec->[$i+3]<<8)|($rec->[$i+2]<<0)); + my $flg = $rec->[$i+4]; + my $ent = $rec->[$i+5]; + my $val = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); + my @ent = ('MODULE','CSECT','INTSYM','XFRADR','GBLSYM','PSECT','IDENT','VSECT'); + if ($ent == 3) { + # XFRADR + $program{START}{PSECT} = $nam; + $program{START}{VALUE} = $val; + } elsif ($ent == 4) { + # GBLSYM flags + $gblsym{$nam}{FLG}{$flg&(1<<0) ? "WEA" : "STR"}++; + $gblsym{$nam}{FLG}{$flg&(1<<3) ? "DEF" : "REF"}++; + $gblsym{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; + $gblsym{$nam}{PSECT} = $psectname; + $gblsym{$nam}{VALUE} = $val; + } elsif ($ent == 5) { + # PSECT flags + $psect[++$psectnumb] = $nam; + $psect{$nam}{NUMBER} = $psectnumb; + $psect{$nam}{FLG}{$flg&(1<<0) ? "GBL" : $flg&(1<<6) ? "GBL" : "LCL"}++; + $psect{$nam}{FLG}{$flg&(1<<2) ? "OVR" : "CAT"}++; + $psect{$nam}{FLG}{$flg&(1<<4) ? "R/O" : "R/W"}++; + $psect{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; + $psect{$nam}{FLG}{$flg&(1<<7) ? "D" : "I/D"}++; + if ($psect{$nam}{FLG}{CAT}) { + $psect{$nam}{LENGTH} = $val; + $psect{$nam}{START} = $psectaddr; + $psectname = $nam; + $psectaddr += $val; + } elsif ($psect{$nam}{FLG}{ABS}) { + $psect{$nam}{LENGTH} = $val; + $psect{$nam}{START} = 0; + $psectname = $nam; + } + } + if ($DEBUG) { + printf $LOG "..GSD: type='%-6s'(%03o) name='%s' value=%06o", $ent[$ent], $ent, $nam, $val; + printf $LOG " flags=%s", join(",", sort(keys(%{$gblsym{$nam}{FLG}}))) if $ent == 4; + printf $LOG " flags=%s", join(",", sort(keys(%{$psect{$nam}{FLG}}))) if $ent == 5; + printf $LOG "\n"; + } + $i += 8; + } + + } elsif ($key == 002) { # ENDGSD + + # just say we saw it + printf $LOG "..ENDGSD\n" if $DEBUG; + + $program{END}{ADDRESS} = 0; + foreach my $nam (sort(keys(%psect))) { + my $start = $psect{$nam}{START}; + my $length = $psect{$nam}{LENGTH}; + my $end = $length ? $start + $length - 1 : $start; + $program{END}{ADDRESS} = $end if $end > $program{END}{ADDRESS}; + printf $LOG "..PSECT[%d](%s) START=%06o END=%06o LENGTH=%06o\n", + $psect{$nam}{NUMBER}, $nam, $start, $end, $length if $length && $DEBUG; + } + $program{START}{ADDRESS} = $program{START}{VALUE} + $psect{$program{START}{PSECT}}{START}; + printf $LOG "..PROG(ADDRESS) START=%06o END=%06o\n", + $program{START}{ADDRESS}, $program{END}{ADDRESS} if $DEBUG; + + } elsif ($key == 003) { # TXT + + # process text record + my $off = ($rec->[3]<<8)|($rec->[2]<<0); + my $len = scalar(@$rec)-4; + my $base = $psect{$psectname}{START}; + my $adr = ($base + $off) & $adrmsk; + foreach my $i (1..$len) { $mem[$adr+$i-1] = $rec->[4+$i-1]; } + if ($DEBUG) { + printf $LOG "..TXT OFFSET=%06o LENGTH=%o BASE=%06o PSECTNAME='%s'\n", $off, $len, $base, $psectname; + for (my $i = 0; $i < $len; $i += 2) { + printf $LOG " %06o: ", ($adr+$i)&~1 if $i%8 == 0; + printf $LOG " %03o...", $mem[$adr+$i++] if ($adr+$i)&1; + printf $LOG " %06o", ($mem[$adr+$i+1]<<8)|($mem[$adr+$i+0]<<0) if $i < $len-1; + printf $LOG " ...%03o", $mem[$adr+$i] if $i == $len-1; + printf $LOG "\n" if $i%8 >= 6 && $i < $len-2; + } + printf $LOG "\n"; + } + $adrmin = $adr if $adrmin eq '' || $adr < $adrmin; + $adrmax = $adr+$len-1 if $adrmax eq '' || $adr+$len-1 > $adrmax; + $textaddr = $adr; + + } elsif ($key == 004) { # RLD + + # iterate over RLD subrecords + for (my $i = 2; $i < scalar(@$rec); ) { + # first byte is entry type and flags + my $ent = $rec->[$i+0] & 0x7F; # entry type + my $flg = $rec->[$i+0] & 0x80; # modification flag (0=word, 1=byte) + # process an entry + if ($ent == 001) { + # internal relocation ... OK + my $dis = $rec->[$i+1]; + my $con = ($rec->[$i+3]<<8)|($rec->[$i+2]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$psectname}{START} + $con); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(IR): adr=%06o val=%06o ; dis=%06o con=%06o\n", + $adr, $val, $dis, $con if $DEBUG; + $i += 4; + } elsif ($ent == 002) { + # global relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + # process + printf $LOG "..RLD(GR): dis=%06o nam='%s'\n", + $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 003) { + # internal displaced ... OK + my $dis = $rec->[$i+1]; + my $con = ($rec->[$i+3]<<8)|($rec->[$i+2]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($con - ($adr+2)); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(ID): adr=%06o val=%06o ; dis=%06o con=%06o\n", + $adr, $val, $dis, $con if $DEBUG; + $i += 4; + } elsif ($ent == 004) { + # global displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + # process + printf $LOG "..RLD(GDR): dis=%06o nam='%s'\n", + $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 005) { + # global additive relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); + # process + printf $LOG "..RLD(GAR): dis=%06o con=%06o nam='%s'\n", + $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 006) { + # global additive displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); + # process + printf $LOG "..RLD(GADR): dis=%06o con=%06o nam='%s'\n", + $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 007) { + # location counter definition ... OK + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); + # process + $psectname = $nam; + $textaddr = $datmsk & ($con); + printf $LOG "..RLD(LCD): adr=%06o ; dis=%06o con=%06o nam='%s'\n", + $textaddr, $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 010) { + # location counter modification ... OK + my $dis = $rec->[$i+1]; + my $con = ($rec->[$i+3]<<8)|($rec->[$i+2]<<0); + # process + $textaddr = $datmsk & ($con); + printf $LOG "..RLD(LCM): adr=%06o ; dis=%06o con=%06o\n", + $textaddr, $dis, $con if $DEBUG; + $i += 4; + } elsif ($ent == 011) { + # program limits ... OK, mostly + my $dis = $rec->[$i+1]; + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ( 01000 ); # make this up, no easy way to compute it + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(LIM1): adr=%06o val=%06o ; dis=%06o\n", + $adr, $val, $dis if $DEBUG; + $dis += 2; + $adr += 2; + $val = $datmsk & ($program{END}{ADDRESS}); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(LIM2): adr=%06o val=%06o ; dis=%06o\n", + $adr, $val, $dis if $DEBUG; + $i += 2; + } elsif ($ent == 012) { + # psect relocation ... OK + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START}); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", + $adr, $val, $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 014) { + # psect displaced relocation ... OK + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START} - ($adr+2)); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PDR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", + $adr, $val, $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 015) { + # psect additive relocation ... OK + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START} + $con); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PAR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", + $adr, $val, $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 016) { + # psect additive displaced relocation ... OK + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START} + $con - ($adr+2)); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PADR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", + $adr, $val, $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 017) { + # complex relocation ... OK + my $dis = $rec->[$i+1]; + my $nam = '. ABS.'; + my $con = 0; + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $loc = 0; + my $val = 0; + my $opc = ""; + my @stk = (); + my $dun = 0; + for ($i += 2; !$dun; $i += 1) { + if ($rec->[$i] == 000) { + $opc = "NOP"; + } elsif ($rec->[$i] == 001) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] + $arg[1]); + $opc = "ADD"; + } elsif ($rec->[$i] == 002) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] - $arg[1]); + $opc = "SUB"; + } elsif ($rec->[$i] == 003) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] * $arg[1]); + $opc = "MUL"; + } elsif ($rec->[$i] == 004) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[1] == 0 ? 0 : int($arg[0] / $arg[1])); + $opc = "DIV"; + } elsif ($rec->[$i] == 005) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] & $arg[1]); + $opc = "AND"; + } elsif ($rec->[$i] == 006) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] | $arg[1]); + $opc = "IOR"; + } elsif ($rec->[$i] == 007) { + my @arg = splice(@stk,-2,2); + push(@stk, $arg[0] ^ $arg[1]); + $opc = "XOR"; + } elsif ($rec->[$i] == 010) { + my @arg = splice(@stk,-1,1); + push(@stk, -$arg[0]); + $opc = "NEG"; + } elsif ($rec->[$i] == 011) { + my @arg = splice(@stk,-1,1); + push(@stk, ~$arg[0]); + $opc = "COM"; + } elsif ($rec->[$i] == 012) { + my @arg = splice(@stk,-1,1); + $val = $arg[0]; + $opc = "STO"; + $dun = 1; + } elsif ($rec->[$i] == 013) { + ############## may need tweaking ################ + my @arg = splice(@stk,-1,1); + $val = $arg[0]; + $opc = "STO+DIS"; + $dun = 1; + } elsif ($rec->[$i] == 016) { + ############## may need tweaking ################ + $nam = &rad2asc(($rec->[$i+2]<<8)|($rec->[$i+1]<<0), + ($rec->[$i+4]<<8)|($rec->[$i+3]<<0)); + $con = $gblsym{$nam}{VALUE}; + push(@stk, $con); + $opc = sprintf("GLB[%s]=(%o)", &trim($nam), $con); + $i += 4; + } elsif ($rec->[$i] == 017) { + $nam = $psect[$rec->[$i+1]]; + $con = ($rec->[$i+3]<<8) | ($rec->[$i+2]<<0); + $loc = $psect{$nam}{START} + $con; + push(@stk, $loc); + $opc = sprintf("FET[%s+%o]=(%o)", &trim($nam), $con, $loc); + $i += 3; + } elsif ($rec->[$i] == 020) { + $con = ($rec->[$i+2]<<8) | ($rec->[$i+1]<<0); + push(@stk, $con); + $opc = "CON"; + $i += 2; + } + $stk[-1] = $datmsk & $stk[-1] if @stk; + printf $LOG "....OPC=%-20s STK=(%s)\n", $opc, join(",",map(sprintf("%o",$_),@stk)) if $DEBUG; + } + printf $LOG "..RLD(CMPX): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; + } else { + die sprintf("Error: Unknown RLD entry 0%o (%d)", $ent, $ent); + } + } + + } elsif ($key == 005) { # ISD + + # ignore + printf $LOG "..ISD: ignored\n" if $DEBUG; + + } elsif ($key == 006) { # ENDMOD + + # just say we saw it + printf $LOG "..ENDMOD\n\n\n" if $DEBUG; + + } elsif ($key == 007) { # LIBHDR + + # ignore + printf $LOG "..LIBHDR: ignored\n" if $DEBUG; + + } elsif ($key == 010) { # LIBEND + + # ignore + printf $LOG "..LIBEND: ignored\n" if $DEBUG; + + } else { # unknown + + # invalid record type in the object file + die sprintf("Error: unknown record type 0%o (%d)", $key, $key); + + } + + return; +} + +#---------------------------------------------------------------------------------------------------- + +# the end From 74a686102cde805e162813ad65d625836aaedac9 Mon Sep 17 00:00:00 2001 From: AK6DN Date: Wed, 5 Apr 2017 15:40:21 -0700 Subject: [PATCH 05/19] Minor type corrections. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c2614b2..fada40c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ If run with no options, it prints a usage screen: ``` -obj2hex.pl v1.5 by Don North (perl 5.022) +obj2hex.pl v2.0 by Don North (perl 5.022) Usage: ./obj2hex.pl [options...] arguments --help output manpage and exit --debug enable debug mode From eeb1147233f51547d46733d755fdf40d97786736 Mon Sep 17 00:00:00 2001 From: AK6DN Date: Fri, 7 Apr 2017 13:20:51 -0700 Subject: [PATCH 06/19] More updates staging for multiple object file linking; change name to obj2bin.pl --- README.md | 16 ++-- obj2hex.pl => obj2bin.pl | 154 ++++++++++++++++++++------------------- 2 files changed, 87 insertions(+), 83 deletions(-) rename obj2hex.pl => obj2bin.pl (97%) diff --git a/README.md b/README.md index fada40c..b0409f6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -obj2hex.pl is a PDP-11 object file translator / linker, transforming an .obj file as output from macro11 into an absolute binary load image file (.bin) or other useful formats (.hex). +obj2bin.pl is a PDP-11 object file translator / linker, transforming an .obj file as output from macro11 into an absolute binary load image file (.bin) or other useful formats (.hex). If run with no options, it prints a usage screen: ``` -obj2hex.pl v2.0 by Don North (perl 5.022) -Usage: ./obj2hex.pl [options...] arguments +obj2bin.pl v2.0 by Don North (perl 5.022) +Usage: ./obj2bin.pl [options...] arguments --help output manpage and exit --debug enable debug mode --verbose verbose status reporting @@ -24,10 +24,10 @@ If run with the --help option it prints a longer manual page: ``` NAME - obj2hex.pl - Convert a Macro-11 program image to PROM/load format + obj2bin.pl - Convert a Macro-11 program image to PROM/load format SYNOPSIS - obj2hex.pl [--help] [--debug] [--verbose] [--boot] [--console] [--binary] + obj2bin.pl [--help] [--debug] [--verbose] [--boot] [--console] [--binary] [--ascii] [--bytes=N] [--nocrc] [--logfile=LOGFILE] --outfile=BINFILE OBJFILE... @@ -122,11 +122,11 @@ ERRORS EXAMPLES Some examples of common usage: - obj2hex.pl --help + obj2bin.pl --help - obj2hex.pl --verbose --boot --out 23-751A9.hex 23-751A9.obj + obj2bin.pl --verbose --boot --out 23-751A9.hex 23-751A9.obj - obj2hex.pl --verbose --binary --out memtest.bin memtest.obj + obj2bin.pl --verbose --binary --out memtest.bin memtest.obj AUTHOR Don North - donorth diff --git a/obj2hex.pl b/obj2bin.pl similarity index 97% rename from obj2hex.pl rename to obj2bin.pl index b27a3fc..983cfa7 100644 --- a/obj2hex.pl +++ b/obj2bin.pl @@ -35,11 +35,11 @@ require 5.008; =head1 NAME -obj2hex.pl - Convert a Macro-11 program image to PROM/load format +obj2bin.pl - Convert a Macro-11 program image to PROM/load format =head1 SYNOPSIS -obj2hex.pl +obj2bin.pl S<[--help]> S<[--debug]> S<[--verbose]> @@ -161,11 +161,11 @@ C -- compare rcv'ed checksum vs exp'e Some examples of common usage: - obj2hex.pl --help + obj2bin.pl --help - obj2hex.pl --verbose --boot --out 23-751A9.hex 23-751A9.obj + obj2bin.pl --verbose --boot --out 23-751A9.hex 23-751A9.obj - obj2hex.pl --verbose --binary --out memtest.bin memtest.obj + obj2bin.pl --verbose --binary --out memtest.bin memtest.obj =head1 AUTHOR @@ -183,6 +183,7 @@ Modification history: 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. 2017-04-01 v2.0 donorth - Started to add capability to process multiple input object files ... still a work in progress. + Renamed from obj2hex.pl to obj2bin.pl =cut @@ -259,7 +260,7 @@ unless ($NOERROR && defined($outfile) && $romtype ne 'NONE' ) { - printf STDERR "obj2hex.pl %s by Don North (perl %g)\n", $VERSION, $]; + printf STDERR "obj2bin.pl %s by Don North (perl %g)\n", $VERSION, $]; print STDERR "Usage: $0 [options...] arguments\n"; print STDERR <<"EOF"; --help output manpage and exit @@ -368,6 +369,7 @@ my $psectnumb = -1; my $textaddr = 0; # program defaults +$program{START}{ADDRESS} = 1; $program{START}{VALUE} = 1; $program{START}{PSECT} = '. ABS.'; @@ -729,11 +731,11 @@ sub parse_rec ($) { $psect[++$psectnumb] = $nam; $psect{$nam}{NUMBER} = $psectnumb; $psect{$nam}{FLG}{$flg&(1<<0) ? "GBL" : $flg&(1<<6) ? "GBL" : "LCL"}++; - $psect{$nam}{FLG}{$flg&(1<<2) ? "OVR" : "CAT"}++; + $psect{$nam}{FLG}{$flg&(1<<2) ? "OVR" : "CON"}++; $psect{$nam}{FLG}{$flg&(1<<4) ? "R/O" : "R/W"}++; $psect{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; $psect{$nam}{FLG}{$flg&(1<<7) ? "D" : "I/D"}++; - if ($psect{$nam}{FLG}{CAT}) { + if ($psect{$nam}{FLG}{CON}) { $psect{$nam}{LENGTH} = $val; $psect{$nam}{START} = $psectaddr; $psectname = $nam; @@ -756,7 +758,7 @@ sub parse_rec ($) { } elsif ($key == 002) { # ENDGSD # just say we saw it - printf $LOG "..ENDGSD\n" if $DEBUG; + printf $LOG "..ENDGSD\n\n" if $DEBUG; $program{END}{ADDRESS} = 0; foreach my $nam (sort(keys(%psect))) { @@ -764,11 +766,13 @@ sub parse_rec ($) { my $length = $psect{$nam}{LENGTH}; my $end = $length ? $start + $length - 1 : $start; $program{END}{ADDRESS} = $end if $end > $program{END}{ADDRESS}; - printf $LOG "..PSECT[%d](%s) START=%06o END=%06o LENGTH=%06o\n", + printf $LOG "....PSECT[%02d](%s) START=%06o END=%06o LENGTH=%06o\n", $psect{$nam}{NUMBER}, $nam, $start, $end, $length if $length && $DEBUG; } - $program{START}{ADDRESS} = $program{START}{VALUE} + $psect{$program{START}{PSECT}}{START}; - printf $LOG "..PROG(ADDRESS) START=%06o END=%06o\n", + if ($program{START}{ADDRESS} == 1) { + $program{START}{ADDRESS} = $program{START}{VALUE} + $psect{$program{START}{PSECT}}{START}; + } + printf $LOG "\n....PROG(ADDRESS) START=%06o END=%06o\n", $program{START}{ADDRESS}, $program{END}{ADDRESS} if $DEBUG; } elsif ($key == 003) { # TXT @@ -814,6 +818,68 @@ sub parse_rec ($) { printf $LOG "..RLD(IR): adr=%06o val=%06o ; dis=%06o con=%06o\n", $adr, $val, $dis, $con if $DEBUG; $i += 4; + } elsif ($ent == 003) { + # internal displaced relocation ... OK + my $dis = $rec->[$i+1]; + my $con = ($rec->[$i+3]<<8)|($rec->[$i+2]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($con - ($adr+2)); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(IDR): adr=%06o val=%06o ; dis=%06o con=%06o\n", + $adr, $val, $dis, $con if $DEBUG; + $i += 4; + } elsif ($ent == 012) { + # psect relocation ... OK + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START}); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", + $adr, $val, $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 014) { + # psect displaced relocation ... OK + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START} - ($adr+2)); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PDR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", + $adr, $val, $dis, $nam if $DEBUG; + $i += 6; + } elsif ($ent == 015) { + # psect additive relocation ... OK + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START} + $con); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PAR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", + $adr, $val, $dis, $con, $nam if $DEBUG; + $i += 8; + } elsif ($ent == 016) { + # psect additive displaced relocation ... OK + my $dis = $rec->[$i+1]; + my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); + # process + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($psect{$nam}{START} + $con - ($adr+2)); + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + printf $LOG "..RLD(PADR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", + $adr, $val, $dis, $con, $nam if $DEBUG; + $i += 8; } elsif ($ent == 002) { # global relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< my $dis = $rec->[$i+1]; @@ -822,18 +888,6 @@ sub parse_rec ($) { printf $LOG "..RLD(GR): dis=%06o nam='%s'\n", $dis, $nam if $DEBUG; $i += 6; - } elsif ($ent == 003) { - # internal displaced ... OK - my $dis = $rec->[$i+1]; - my $con = ($rec->[$i+3]<<8)|($rec->[$i+2]<<0); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($con - ($adr+2)); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(ID): adr=%06o val=%06o ; dis=%06o con=%06o\n", - $adr, $val, $dis, $con if $DEBUG; - $i += 4; } elsif ($ent == 004) { # global displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< my $dis = $rec->[$i+1]; @@ -898,56 +952,6 @@ sub parse_rec ($) { printf $LOG "..RLD(LIM2): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; $i += 2; - } elsif ($ent == 012) { - # psect relocation ... OK - my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($psect{$nam}{START}); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(PR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", - $adr, $val, $dis, $nam if $DEBUG; - $i += 6; - } elsif ($ent == 014) { - # psect displaced relocation ... OK - my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($psect{$nam}{START} - ($adr+2)); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(PDR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", - $adr, $val, $dis, $nam if $DEBUG; - $i += 6; - } elsif ($ent == 015) { - # psect additive relocation ... OK - my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); - my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($psect{$nam}{START} + $con); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(PAR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", - $adr, $val, $dis, $con, $nam if $DEBUG; - $i += 8; - } elsif ($ent == 016) { - # psect additive displaced relocation ... OK - my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); - my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); - # process - my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($psect{$nam}{START} + $con - ($adr+2)); - $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); - $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); - printf $LOG "..RLD(PADR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", - $adr, $val, $dis, $con, $nam if $DEBUG; - $i += 8; } elsif ($ent == 017) { # complex relocation ... OK my $dis = $rec->[$i+1]; @@ -1034,7 +1038,7 @@ sub parse_rec ($) { $stk[-1] = $datmsk & $stk[-1] if @stk; printf $LOG "....OPC=%-20s STK=(%s)\n", $opc, join(",",map(sprintf("%o",$_),@stk)) if $DEBUG; } - printf $LOG "..RLD(CMPX): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; + printf $LOG "..RLD(CPXR): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; } else { die sprintf("Error: Unknown RLD entry 0%o (%d)", $ent, $ent); } From 90261543f47a186714665cdb6ddfef0767424e9c Mon Sep 17 00:00:00 2001 From: AK6DN Date: Fri, 7 Apr 2017 13:29:59 -0700 Subject: [PATCH 07/19] Updated readme file --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b0409f6..e0de6d7 100644 --- a/README.md +++ b/README.md @@ -142,4 +142,5 @@ HISTORY 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. 2017-04-01 v2.0 donorth - Started to add capability to process multiple input object files ... still a work in progress. + Renamed from obj2hex.pl to obj2bin.pl ``` From 364bd9603ef3177422e4dfc7c1a5473c67ac989c Mon Sep 17 00:00:00 2001 From: AK6DN Date: Sat, 8 Apr 2017 00:04:35 -0700 Subject: [PATCH 08/19] Add debugging output for global symbol evaluation --- obj2bin.pl | 123 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 50 deletions(-) diff --git a/obj2bin.pl b/obj2bin.pl index 983cfa7..2aaa1a1 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -291,8 +291,9 @@ sub trim ($); sub chksum (@); sub rad2asc (@); sub crc (%); +sub sym2psect ($$); sub read_rec ($); -sub parse_rec ($); +sub parse_rec ($$$); #---------------------------------------------------------------------------------------------------- @@ -363,7 +364,7 @@ my %gblsym = (); my %psect = (); my @psect = (); my %program = (); -my $psectname = '. ABS.'; +my $psectname = sprintf("%02d:%s",1,'. ABS.'); my $psectaddr = 0; my $psectnumb = -1; my $textaddr = 0; @@ -371,20 +372,18 @@ my $textaddr = 0; # program defaults $program{START}{ADDRESS} = 1; $program{START}{VALUE} = 1; -$program{START}{PSECT} = '. ABS.'; +$program{START}{PSECT} = $psectname; -# process all object files -while (my $objfile = shift(@ARGV)) { - - # open the input .obj file, die if error - my $OBJ = FileHandle->new("< ".$objfile); - die "Error: can't open input object file '$objfile'\n" unless defined $OBJ; - - # now parse all the records - while (my @rec = &read_rec($OBJ)) { &parse_rec(\@rec); } - - # done with object file - $OBJ->close; +# two passes, first is headers, second is data records +foreach my $pass (1..2) { + foreach my $numb (0..$#ARGV) { + my $objfile = $ARGV[$numb]; + my $OBJ = FileHandle->new("< ".$objfile); + die "Error: can't open input object file '$objfile'\n" unless defined $OBJ; + printf $LOG "\n\nPROCESS PASS %d FILE %d '%s'\n\n", $pass, $numb+1, $objfile if $DEBUG; + while (my @rec = &read_rec($OBJ)) { &parse_rec($numb+1, $pass, \@rec); } + $OBJ->close; + } } #---------------------------------------------------------------------------------------------------- @@ -574,6 +573,15 @@ sub rad2asc (@) { #---------------------------------------------------------------------------------------------------- +# symbol to psect name converter + +sub sym2psect ($$) { + + return sprintf("%02d:%-6s", @_); +} + +#---------------------------------------------------------------------------------------------------- + # crc computation routine sub crc (%) { @@ -669,7 +677,7 @@ sub read_rec ($) { push(@suf, ord($buf)); # output the record if debugging - if ($DEBUG) { + if ($DEBUG >= 2) { my $fmt = "%03o"; my $n = 16; my $pre = sprintf("RECORD: [%s] ",join(" ",map(sprintf($fmt,$_),@pre))); @@ -698,34 +706,37 @@ sub read_rec ($) { # parse an input object file record, update data structures -sub parse_rec ($) { +sub parse_rec ($$$) { - my ($rec) = (@_); + my ($file,$pass,$rec) = (@_); # type is first byte of record my $key = $rec->[0]; - if ($key == 001) { # GSD + if ($key == 001 && $pass == 1) { # GSD # iterate over GSD subrecords for (my $i = 2; $i < scalar(@$rec); ) { # GSD records are fixed 8B length all in the same format - my $nam = &rad2asc(($rec->[$i+1]<<8)|($rec->[$i+0]<<0), ($rec->[$i+3]<<8)|($rec->[$i+2]<<0)); + my $sym = &rad2asc(($rec->[$i+1]<<8)|($rec->[$i+0]<<0),($rec->[$i+3]<<8)|($rec->[$i+2]<<0)); + my $nam = &sym2psect($file,$sym); my $flg = $rec->[$i+4]; my $ent = $rec->[$i+5]; my $val = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); my @ent = ('MODULE','CSECT','INTSYM','XFRADR','GBLSYM','PSECT','IDENT','VSECT'); + my $def = undef; if ($ent == 3) { # XFRADR $program{START}{PSECT} = $nam; $program{START}{VALUE} = $val; } elsif ($ent == 4) { # GBLSYM flags - $gblsym{$nam}{FLG}{$flg&(1<<0) ? "WEA" : "STR"}++; - $gblsym{$nam}{FLG}{$flg&(1<<3) ? "DEF" : "REF"}++; - $gblsym{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; - $gblsym{$nam}{PSECT} = $psectname; - $gblsym{$nam}{VALUE} = $val; + $def = $flg&(1<<3) ? "DEF" : "REF"; + $gblsym{$sym}{$def}{FLG}{$flg&(1<<0) ? "WEA" : "STR"}++; + $gblsym{$sym}{$def}{FLG}{$flg&(1<<3) ? "DEF" : "REF"}++; + $gblsym{$sym}{$def}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; + $gblsym{$sym}{$def}{PSECT} = $psectname; + $gblsym{$sym}{$def}{VALUE} = $val; } elsif ($ent == 5) { # PSECT flags $psect[++$psectnumb] = $nam; @@ -747,15 +758,17 @@ sub parse_rec ($) { } } if ($DEBUG) { - printf $LOG "..GSD: type='%-6s'(%03o) name='%s' value=%06o", $ent[$ent], $ent, $nam, $val; - printf $LOG " flags=%s", join(",", sort(keys(%{$gblsym{$nam}{FLG}}))) if $ent == 4; + printf $LOG "..GSD: type='%-6s'(%03o) name='%s' value=%06o", $ent[$ent], $ent, ($ent == 4 ? $sym : $nam), $val; + printf $LOG " psect='%s' value=%06o", $gblsym{$sym}{$def}{PSECT}, $gblsym{$sym}{$def}{VALUE} if $ent == 4; + printf $LOG " length=%06o start=%06o", $psect{$nam}{LENGTH}, $psect{$nam}{START} if $ent == 5; + printf $LOG " flags=%s", join(",", sort(keys(%{$gblsym{$sym}{$def}{FLG}}))) if $ent == 4; printf $LOG " flags=%s", join(",", sort(keys(%{$psect{$nam}{FLG}}))) if $ent == 5; printf $LOG "\n"; } $i += 8; } - } elsif ($key == 002) { # ENDGSD + } elsif ($key == 002 && $pass == 1) { # ENDGSD # just say we saw it printf $LOG "..ENDGSD\n\n" if $DEBUG; @@ -769,13 +782,23 @@ sub parse_rec ($) { printf $LOG "....PSECT[%02d](%s) START=%06o END=%06o LENGTH=%06o\n", $psect{$nam}{NUMBER}, $nam, $start, $end, $length if $length && $DEBUG; } + + printf $LOG "\n"; + foreach my $nam (sort(keys(%gblsym))) { + if (exists $gblsym{$nam}{DEF}) { + my $address = $gblsym{$nam}{DEF}{VALUE} + $psect{$gblsym{$nam}{DEF}{PSECT}}{START}; + printf $LOG "....GBLSYM(%s) PSECT='%s' VALUE=%06o : ADDRESS=%06o\n", + $nam, $gblsym{$nam}{DEF}{PSECT}, $gblsym{$nam}{DEF}{VALUE}, $address if $DEBUG; + } + } + if ($program{START}{ADDRESS} == 1) { $program{START}{ADDRESS} = $program{START}{VALUE} + $psect{$program{START}{PSECT}}{START}; } printf $LOG "\n....PROG(ADDRESS) START=%06o END=%06o\n", $program{START}{ADDRESS}, $program{END}{ADDRESS} if $DEBUG; - } elsif ($key == 003) { # TXT + } elsif ($key == 003 && $pass == 2) { # TXT # process text record my $off = ($rec->[3]<<8)|($rec->[2]<<0); @@ -798,7 +821,7 @@ sub parse_rec ($) { $adrmax = $adr+$len-1 if $adrmax eq '' || $adr+$len-1 > $adrmax; $textaddr = $adr; - } elsif ($key == 004) { # RLD + } elsif ($key == 004 && $pass == 2) { # RLD # iterate over RLD subrecords for (my $i = 2; $i < scalar(@$rec); ) { @@ -833,7 +856,7 @@ sub parse_rec ($) { } elsif ($ent == 012) { # psect relocation ... OK my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $nam = &sym2psect($file,&rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0))); # process my $adr = $adrmsk & ($textaddr + $dis - 4); my $val = $datmsk & ($psect{$nam}{START}); @@ -845,7 +868,7 @@ sub parse_rec ($) { } elsif ($ent == 014) { # psect displaced relocation ... OK my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $nam = &sym2psect($file,&rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0))); # process my $adr = $adrmsk & ($textaddr + $dis - 4); my $val = $datmsk & ($psect{$nam}{START} - ($adr+2)); @@ -857,7 +880,7 @@ sub parse_rec ($) { } elsif ($ent == 015) { # psect additive relocation ... OK my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $nam = &sym2psect($file,&rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0))); my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); # process my $adr = $adrmsk & ($textaddr + $dis - 4); @@ -870,7 +893,7 @@ sub parse_rec ($) { } elsif ($ent == 016) { # psect additive displaced relocation ... OK my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $nam = &sym2psect($file,&rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0))); my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); # process my $adr = $adrmsk & ($textaddr + $dis - 4); @@ -883,41 +906,41 @@ sub parse_rec ($) { } elsif ($ent == 002) { # global relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); # process - printf $LOG "..RLD(GR): dis=%06o nam='%s'\n", - $dis, $nam if $DEBUG; + printf $LOG "..RLD(GR): adr=?????? val=?????? ; dis=%06o sym='%s'\n", + $dis, $sym if $DEBUG; $i += 6; } elsif ($ent == 004) { # global displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); # process - printf $LOG "..RLD(GDR): dis=%06o nam='%s'\n", - $dis, $nam if $DEBUG; + printf $LOG "..RLD(GDR): adr=?????? val=?????? ; dis=%06o sym='%s'\n", + $dis, $sym if $DEBUG; $i += 6; } elsif ($ent == 005) { # global additive relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); # process - printf $LOG "..RLD(GAR): dis=%06o con=%06o nam='%s'\n", - $dis, $con, $nam if $DEBUG; + printf $LOG "..RLD(GAR): adr=?????? val=?????? ; dis=%06o con=%06o sym='%s'\n", + $dis, $con, $sym if $DEBUG; $i += 8; } elsif ($ent == 006) { # global additive displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); # process - printf $LOG "..RLD(GADR): dis=%06o con=%06o nam='%s'\n", - $dis, $con, $nam if $DEBUG; + printf $LOG "..RLD(GADR): adr=?????? val=?????? ; dis=%06o con=%06o sym='%s'\n", + $dis, $con, $sym if $DEBUG; $i += 8; } elsif ($ent == 007) { # location counter definition ... OK my $dis = $rec->[$i+1]; - my $nam = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0), ($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); + my $nam = &sym2psect($file,&rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0))); my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); # process $psectname = $nam; @@ -955,7 +978,7 @@ sub parse_rec ($) { } elsif ($ent == 017) { # complex relocation ... OK my $dis = $rec->[$i+1]; - my $nam = '. ABS.'; + my $nam = &sym2psect($file,'. ABS.'); my $con = 0; # process my $adr = $adrmsk & ($textaddr + $dis - 4); @@ -1018,7 +1041,7 @@ sub parse_rec ($) { ############## may need tweaking ################ $nam = &rad2asc(($rec->[$i+2]<<8)|($rec->[$i+1]<<0), ($rec->[$i+4]<<8)|($rec->[$i+3]<<0)); - $con = $gblsym{$nam}{VALUE}; + $con = $gblsym{$nam}{DEF}{VALUE}; push(@stk, $con); $opc = sprintf("GLB[%s]=(%o)", &trim($nam), $con); $i += 4; @@ -1064,7 +1087,7 @@ sub parse_rec ($) { # ignore printf $LOG "..LIBEND: ignored\n" if $DEBUG; - } else { # unknown + } elsif ($key == 000 || $key >= 011) { # unknown # invalid record type in the object file die sprintf("Error: unknown record type 0%o (%d)", $key, $key); From c4f9db5d8f17c47ae57b175b52f0f3e46214b085 Mon Sep 17 00:00:00 2001 From: AK6DN Date: Sat, 8 Apr 2017 02:49:22 -0700 Subject: [PATCH 09/19] First version with support for multiple .obj files and linking global symbols -- preliminary, lightly tested --- obj2bin.pl | 77 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/obj2bin.pl b/obj2bin.pl index 2aaa1a1..a645aac 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -783,12 +783,12 @@ sub parse_rec ($$$) { $psect{$nam}{NUMBER}, $nam, $start, $end, $length if $length && $DEBUG; } - printf $LOG "\n"; + printf $LOG "\n" if $DEBUG; foreach my $nam (sort(keys(%gblsym))) { if (exists $gblsym{$nam}{DEF}) { - my $address = $gblsym{$nam}{DEF}{VALUE} + $psect{$gblsym{$nam}{DEF}{PSECT}}{START}; + $gblsym{$nam}{DEF}{ADDRESS} = $gblsym{$nam}{DEF}{VALUE} + $psect{$gblsym{$nam}{DEF}{PSECT}}{START}; printf $LOG "....GBLSYM(%s) PSECT='%s' VALUE=%06o : ADDRESS=%06o\n", - $nam, $gblsym{$nam}{DEF}{PSECT}, $gblsym{$nam}{DEF}{VALUE}, $address if $DEBUG; + $nam, $gblsym{$nam}{DEF}{PSECT}, $gblsym{$nam}{DEF}{VALUE}, $gblsym{$nam}{DEF}{ADDRESS} if $DEBUG; } } @@ -836,8 +836,10 @@ sub parse_rec ($$$) { # process my $adr = $adrmsk & ($textaddr + $dis - 4); my $val = $datmsk & ($psect{$psectname}{START} + $con); + # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print printf $LOG "..RLD(IR): adr=%06o val=%06o ; dis=%06o con=%06o\n", $adr, $val, $dis, $con if $DEBUG; $i += 4; @@ -848,8 +850,10 @@ sub parse_rec ($$$) { # process my $adr = $adrmsk & ($textaddr + $dis - 4); my $val = $datmsk & ($con - ($adr+2)); + # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print printf $LOG "..RLD(IDR): adr=%06o val=%06o ; dis=%06o con=%06o\n", $adr, $val, $dis, $con if $DEBUG; $i += 4; @@ -860,8 +864,10 @@ sub parse_rec ($$$) { # process my $adr = $adrmsk & ($textaddr + $dis - 4); my $val = $datmsk & ($psect{$nam}{START}); + # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print printf $LOG "..RLD(PR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", $adr, $val, $dis, $nam if $DEBUG; $i += 6; @@ -872,8 +878,10 @@ sub parse_rec ($$$) { # process my $adr = $adrmsk & ($textaddr + $dis - 4); my $val = $datmsk & ($psect{$nam}{START} - ($adr+2)); + # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print printf $LOG "..RLD(PDR): adr=%06o val=%06o ; dis=%06o nam='%s'\n", $adr, $val, $dis, $nam if $DEBUG; $i += 6; @@ -885,8 +893,10 @@ sub parse_rec ($$$) { # process my $adr = $adrmsk & ($textaddr + $dis - 4); my $val = $datmsk & ($psect{$nam}{START} + $con); + # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print printf $LOG "..RLD(PAR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", $adr, $val, $dis, $con, $nam if $DEBUG; $i += 8; @@ -898,44 +908,70 @@ sub parse_rec ($$$) { # process my $adr = $adrmsk & ($textaddr + $dis - 4); my $val = $datmsk & ($psect{$nam}{START} + $con - ($adr+2)); + # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print printf $LOG "..RLD(PADR): adr=%06o val=%06o ; dis=%06o con=%06o nam='%s'\n", $adr, $val, $dis, $con, $nam if $DEBUG; $i += 8; } elsif ($ent == 002) { - # global relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # global relocation ... OK my $dis = $rec->[$i+1]; my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); # process - printf $LOG "..RLD(GR): adr=?????? val=?????? ; dis=%06o sym='%s'\n", - $dis, $sym if $DEBUG; + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($gblsym{$sym}{DEF}{ADDRESS}); + # store + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print + printf $LOG "..RLD(GR): adr=%06o val=%06o ; dis=%06o sym='%s'\n", + $adr, $val, $dis, $sym if $DEBUG; $i += 6; } elsif ($ent == 004) { - # global displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # global displaced relocation ... OK my $dis = $rec->[$i+1]; my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); # process - printf $LOG "..RLD(GDR): adr=?????? val=?????? ; dis=%06o sym='%s'\n", - $dis, $sym if $DEBUG; + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($gblsym{$sym}{DEF}{ADDRESS} - ($adr+2)); + # store + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print + printf $LOG "..RLD(GDR): adr=%06o val=%06o ; dis=%06o sym='%s'\n", + $adr, $val, $dis, $sym if $DEBUG; $i += 6; } elsif ($ent == 005) { - # global additive relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # global additive relocation ... OK my $dis = $rec->[$i+1]; my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); # process - printf $LOG "..RLD(GAR): adr=?????? val=?????? ; dis=%06o con=%06o sym='%s'\n", - $dis, $con, $sym if $DEBUG; + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($gblsym{$sym}{DEF}{ADDRESS} + $con); + # store + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print + printf $LOG "..RLD(GAR): adr=%06o val=%06o ; dis=%06o con=%06o sym='%s'\n", + $adr, $val, $dis, $con, $sym if $DEBUG; $i += 8; } elsif ($ent == 006) { - # global additive displaced relocation ... TBD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # global additive displaced relocation ... OK my $dis = $rec->[$i+1]; my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); # process - printf $LOG "..RLD(GADR): adr=?????? val=?????? ; dis=%06o con=%06o sym='%s'\n", - $dis, $con, $sym if $DEBUG; + my $adr = $adrmsk & ($textaddr + $dis - 4); + my $val = $datmsk & ($gblsym{$sym}{DEF}{ADDRESS} + $con - ($adr+2)); + # store + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print + printf $LOG "..RLD(GADR): adr=%06o val=%06o ; dis=%06o con=%06o sym='%s'\n", + $adr, $val, $dis, $con, $sym if $DEBUG; $i += 8; } elsif ($ent == 007) { # location counter definition ... OK @@ -945,6 +981,7 @@ sub parse_rec ($$$) { # process $psectname = $nam; $textaddr = $datmsk & ($con); + # print printf $LOG "..RLD(LCD): adr=%06o ; dis=%06o con=%06o nam='%s'\n", $textaddr, $dis, $con, $nam if $DEBUG; $i += 8; @@ -954,6 +991,7 @@ sub parse_rec ($$$) { my $con = ($rec->[$i+3]<<8)|($rec->[$i+2]<<0); # process $textaddr = $datmsk & ($con); + # print printf $LOG "..RLD(LCM): adr=%06o ; dis=%06o con=%06o\n", $textaddr, $dis, $con if $DEBUG; $i += 4; @@ -963,15 +1001,20 @@ sub parse_rec ($$$) { # process my $adr = $adrmsk & ($textaddr + $dis - 4); my $val = $datmsk & ( 01000 ); # make this up, no easy way to compute it + # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print printf $LOG "..RLD(LIM1): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; + # process $dis += 2; $adr += 2; $val = $datmsk & ($program{END}{ADDRESS}); + # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); + # print printf $LOG "..RLD(LIM2): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; $i += 2; @@ -1032,13 +1075,13 @@ sub parse_rec ($$$) { $opc = "STO"; $dun = 1; } elsif ($rec->[$i] == 013) { - ############## may need tweaking ################ + ############## may need tweaking ################ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< my @arg = splice(@stk,-1,1); $val = $arg[0]; $opc = "STO+DIS"; $dun = 1; } elsif ($rec->[$i] == 016) { - ############## may need tweaking ################ + ############## may need tweaking ################ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $nam = &rad2asc(($rec->[$i+2]<<8)|($rec->[$i+1]<<0), ($rec->[$i+4]<<8)|($rec->[$i+3]<<0)); $con = $gblsym{$nam}{DEF}{VALUE}; From ac453f8a0b4cf0883613c25cb04cb20c5cfc7eee Mon Sep 17 00:00:00 2001 From: AK6DN Date: Sat, 8 Apr 2017 14:29:46 -0700 Subject: [PATCH 10/19] Added global symbol redefinition/undefined warnings; reformat debug output; change some die to warn --- obj2bin.pl | 58 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/obj2bin.pl b/obj2bin.pl index a645aac..85cdd8c 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -293,6 +293,7 @@ sub rad2asc (@); sub crc (%); sub sym2psect ($$); sub read_rec ($); +sub get_global ($); sub parse_rec ($$$); #---------------------------------------------------------------------------------------------------- @@ -692,11 +693,11 @@ sub read_rec ($) { } # check we have a well formatted record - die sprintf("Error: invalid object file record format (%d)", $err) if $err; + warn sprintf("Warning: invalid object file record format (%d)", $err) if $err; # compare rcv'ed checksum vs exp'ed checksum my $exp = &chksum(0x01, $len>>0, $len>>8, @dat); - die sprintf("Error: Bad checksum exp=0x%02X rcv=0x%02X", $exp, $rcv) unless $exp == $rcv; + warn sprintf("Warning: Bad checksum exp=0x%02X rcv=0x%02X", $exp, $rcv) unless $exp == $rcv; # all is well, return the record return @dat; @@ -704,6 +705,24 @@ sub read_rec ($) { #---------------------------------------------------------------------------------------------------- +# get a global symbol target value + +sub get_global ($) { + + my ($sym) = @_; + + # return target value if exists + return $gblsym{$sym}{DEF}{ADDRESS} if exists $gblsym{$sym}{DEF}{ADDRESS}; + + # issue a warning for multiple definition with a different address + warn sprintf("Warning: global symbol undefined: symbol=%s, assuming value of 000000\n", $sym); + + # and return nil + return 0; +} + +#---------------------------------------------------------------------------------------------------- + # parse an input object file record, update data structures sub parse_rec ($$$) { @@ -731,12 +750,21 @@ sub parse_rec ($$$) { $program{START}{VALUE} = $val; } elsif ($ent == 4) { # GBLSYM flags + my $adr = $val + $psect{$psectname}{START}; $def = $flg&(1<<3) ? "DEF" : "REF"; - $gblsym{$sym}{$def}{FLG}{$flg&(1<<0) ? "WEA" : "STR"}++; - $gblsym{$sym}{$def}{FLG}{$flg&(1<<3) ? "DEF" : "REF"}++; - $gblsym{$sym}{$def}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; - $gblsym{$sym}{$def}{PSECT} = $psectname; - $gblsym{$sym}{$def}{VALUE} = $val; + if ($def eq "DEF" && exists $gblsym{$sym}{$def} && $adr != $gblsym{$sym}{$def}{ADDRESS}) { + # issue a warning for multiple definition with a different address + warn sprintf("Warning: attempted global symbol redefinition: symbol=%s (address/psect) old=%06o/%s new=%06o/%s -- IGNORING\n", + &trim($sym), $gblsym{$sym}{$def}{ADDRESS}, &trim($gblsym{$sym}{$def}{PSECT}), $adr, &trim($psectname)); + } else { + # define first time only ... ignore any redefinition attempt + $gblsym{$sym}{$def}{FLG}{$flg&(1<<0) ? "WEA" : "STR"}++; + $gblsym{$sym}{$def}{FLG}{$flg&(1<<3) ? "DEF" : "REF"}++; + $gblsym{$sym}{$def}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; + $gblsym{$sym}{$def}{PSECT} = $psectname; + $gblsym{$sym}{$def}{VALUE} = $val; + $gblsym{$sym}{$def}{ADDRESS} = $adr; + } } elsif ($ent == 5) { # PSECT flags $psect[++$psectnumb] = $nam; @@ -786,7 +814,6 @@ sub parse_rec ($$$) { printf $LOG "\n" if $DEBUG; foreach my $nam (sort(keys(%gblsym))) { if (exists $gblsym{$nam}{DEF}) { - $gblsym{$nam}{DEF}{ADDRESS} = $gblsym{$nam}{DEF}{VALUE} + $psect{$gblsym{$nam}{DEF}{PSECT}}{START}; printf $LOG "....GBLSYM(%s) PSECT='%s' VALUE=%06o : ADDRESS=%06o\n", $nam, $gblsym{$nam}{DEF}{PSECT}, $gblsym{$nam}{DEF}{VALUE}, $gblsym{$nam}{DEF}{ADDRESS} if $DEBUG; } @@ -921,7 +948,7 @@ sub parse_rec ($$$) { my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); # process my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($gblsym{$sym}{DEF}{ADDRESS}); + my $val = $datmsk & (&get_global($sym)); # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); @@ -935,7 +962,7 @@ sub parse_rec ($$$) { my $sym = &rad2asc(($rec->[$i+3]<<8)|($rec->[$i+2]<<0),($rec->[$i+5]<<8)|($rec->[$i+4]<<0)); # process my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($gblsym{$sym}{DEF}{ADDRESS} - ($adr+2)); + my $val = $datmsk & (&get_global($sym) - ($adr+2)); # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); @@ -950,7 +977,7 @@ sub parse_rec ($$$) { my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); # process my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($gblsym{$sym}{DEF}{ADDRESS} + $con); + my $val = $datmsk & (&get_global($sym) + $con); # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); @@ -965,7 +992,7 @@ sub parse_rec ($$$) { my $con = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); # process my $adr = $adrmsk & ($textaddr + $dis - 4); - my $val = $datmsk & ($gblsym{$sym}{DEF}{ADDRESS} + $con - ($adr+2)); + my $val = $datmsk & (&get_global($sym) + $con - ($adr+2)); # store $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); @@ -1070,6 +1097,7 @@ sub parse_rec ($$$) { push(@stk, ~$arg[0]); $opc = "COM"; } elsif ($rec->[$i] == 012) { + ############## may need tweaking ################ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< my @arg = splice(@stk,-1,1); $val = $arg[0]; $opc = "STO"; @@ -1084,7 +1112,7 @@ sub parse_rec ($$$) { ############## may need tweaking ################ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $nam = &rad2asc(($rec->[$i+2]<<8)|($rec->[$i+1]<<0), ($rec->[$i+4]<<8)|($rec->[$i+3]<<0)); - $con = $gblsym{$nam}{DEF}{VALUE}; + $con = &get_global($nam); push(@stk, $con); $opc = sprintf("GLB[%s]=(%o)", &trim($nam), $con); $i += 4; @@ -1106,7 +1134,7 @@ sub parse_rec ($$$) { } printf $LOG "..RLD(CPXR): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; } else { - die sprintf("Error: Unknown RLD entry 0%o (%d)", $ent, $ent); + warn sprintf("Warning: Unknown RLD entry 0%o (%d)", $ent, $ent); } } @@ -1133,7 +1161,7 @@ sub parse_rec ($$$) { } elsif ($key == 000 || $key >= 011) { # unknown # invalid record type in the object file - die sprintf("Error: unknown record type 0%o (%d)", $key, $key); + warn sprintf("Warning: unknown record type 0%o (%d)", $key, $key); } From f58c4268f95386ee5171eb97e0e8b575264dca2f Mon Sep 17 00:00:00 2001 From: AK6DN Date: Sat, 8 Apr 2017 22:46:48 -0700 Subject: [PATCH 11/19] Update complex relocation evaluation so that it now works, and stores correct value at correct location --- obj2bin.pl | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/obj2bin.pl b/obj2bin.pl index 85cdd8c..26452cb 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -1051,7 +1051,7 @@ sub parse_rec ($$$) { my $nam = &sym2psect($file,'. ABS.'); my $con = 0; # process - my $adr = $adrmsk & ($textaddr + $dis - 4); + my $adr = 0; my $loc = 0; my $val = 0; my $opc = ""; @@ -1059,64 +1059,76 @@ sub parse_rec ($$$) { my $dun = 0; for ($i += 2; !$dun; $i += 1) { if ($rec->[$i] == 000) { + # NOP do nothing $opc = "NOP"; } elsif ($rec->[$i] == 001) { + # ADD : pop + pop => push my @arg = splice(@stk,-2,2); push(@stk, $arg[0] + $arg[1]); $opc = "ADD"; } elsif ($rec->[$i] == 002) { + # SUB : pop - pop => push my @arg = splice(@stk,-2,2); push(@stk, $arg[0] - $arg[1]); $opc = "SUB"; } elsif ($rec->[$i] == 003) { + # MUL : pop * pop => push my @arg = splice(@stk,-2,2); push(@stk, $arg[0] * $arg[1]); $opc = "MUL"; } elsif ($rec->[$i] == 004) { + # DIV : pop / pop => push my @arg = splice(@stk,-2,2); push(@stk, $arg[1] == 0 ? 0 : int($arg[0] / $arg[1])); $opc = "DIV"; } elsif ($rec->[$i] == 005) { + # AND : pop & pop => push my @arg = splice(@stk,-2,2); push(@stk, $arg[0] & $arg[1]); $opc = "AND"; } elsif ($rec->[$i] == 006) { + # IOR : pop | pop => push my @arg = splice(@stk,-2,2); push(@stk, $arg[0] | $arg[1]); $opc = "IOR"; } elsif ($rec->[$i] == 007) { + # XOR : pop ^ pop => push my @arg = splice(@stk,-2,2); push(@stk, $arg[0] ^ $arg[1]); $opc = "XOR"; } elsif ($rec->[$i] == 010) { + # NEG : pop - => push my @arg = splice(@stk,-1,1); push(@stk, -$arg[0]); $opc = "NEG"; } elsif ($rec->[$i] == 011) { + # COM : pop ~ => push my @arg = splice(@stk,-1,1); push(@stk, ~$arg[0]); $opc = "COM"; } elsif ($rec->[$i] == 012) { - ############## may need tweaking ################ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # STO : pop => store @ address my @arg = splice(@stk,-1,1); - $val = $arg[0]; + $adr = $adrmsk & ($textaddr + $dis - 4); + $val = $datmsk & ($arg[0]); $opc = "STO"; $dun = 1; } elsif ($rec->[$i] == 013) { - ############## may need tweaking ################ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # STO : pop => store @ address + disp my @arg = splice(@stk,-1,1); - $val = $arg[0]; + $adr = $adrmsk & ($textaddr + $dis - 4); + $val = $datmsk & ($arg[0] - ($adr+2)); $opc = "STO+DIS"; $dun = 1; } elsif ($rec->[$i] == 016) { - ############## may need tweaking ################ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - $nam = &rad2asc(($rec->[$i+2]<<8)|($rec->[$i+1]<<0), - ($rec->[$i+4]<<8)|($rec->[$i+3]<<0)); + # FET : global => push + $nam = &rad2asc(($rec->[$i+2]<<8)|($rec->[$i+1]<<0),($rec->[$i+4]<<8)|($rec->[$i+3]<<0)); $con = &get_global($nam); push(@stk, $con); $opc = sprintf("GLB[%s]=(%o)", &trim($nam), $con); $i += 4; } elsif ($rec->[$i] == 017) { + # FET : local => push $nam = $psect[$rec->[$i+1]]; $con = ($rec->[$i+3]<<8) | ($rec->[$i+2]<<0); $loc = $psect{$nam}{START} + $con; @@ -1124,6 +1136,7 @@ sub parse_rec ($$$) { $opc = sprintf("FET[%s+%o]=(%o)", &trim($nam), $con, $loc); $i += 3; } elsif ($rec->[$i] == 020) { + # CONstant : value => push $con = ($rec->[$i+2]<<8) | ($rec->[$i+1]<<0); push(@stk, $con); $opc = "CON"; @@ -1132,7 +1145,11 @@ sub parse_rec ($$$) { $stk[-1] = $datmsk & $stk[-1] if @stk; printf $LOG "....OPC=%-20s STK=(%s)\n", $opc, join(",",map(sprintf("%o",$_),@stk)) if $DEBUG; } + # print printf $LOG "..RLD(CPXR): adr=%06o val=%06o ; dis=%06o\n", $adr, $val, $dis if $DEBUG; + # store + $mem[($adr+0)&$adrmsk] = $memmsk & ($val>>0); + $mem[($adr+1)&$adrmsk] = $memmsk & ($val>>8); } else { warn sprintf("Warning: Unknown RLD entry 0%o (%d)", $ent, $ent); } From 688828abd09dadd15258fe9c3e585a3a48c55a26 Mon Sep 17 00:00:00 2001 From: AK6DN Date: Sun, 9 Apr 2017 17:55:34 -0700 Subject: [PATCH 12/19] Always block out a psect length to be even --- obj2bin.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/obj2bin.pl b/obj2bin.pl index 26452cb..312567f 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -754,7 +754,7 @@ sub parse_rec ($$$) { $def = $flg&(1<<3) ? "DEF" : "REF"; if ($def eq "DEF" && exists $gblsym{$sym}{$def} && $adr != $gblsym{$sym}{$def}{ADDRESS}) { # issue a warning for multiple definition with a different address - warn sprintf("Warning: attempted global symbol redefinition: symbol=%s (address/psect) old=%06o/%s new=%06o/%s -- IGNORING\n", + warn sprintf("Warning: global symbol redefinition: symbol=%s (address/psect) old=%06o/%s new=%06o/%s -- IGNORING\n", &trim($sym), $gblsym{$sym}{$def}{ADDRESS}, &trim($gblsym{$sym}{$def}{PSECT}), $adr, &trim($psectname)); } else { # define first time only ... ignore any redefinition attempt @@ -774,6 +774,7 @@ sub parse_rec ($$$) { $psect{$nam}{FLG}{$flg&(1<<4) ? "R/O" : "R/W"}++; $psect{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; $psect{$nam}{FLG}{$flg&(1<<7) ? "D" : "I/D"}++; + $val++ if $val & 1; if ($psect{$nam}{FLG}{CON}) { $psect{$nam}{LENGTH} = $val; $psect{$nam}{START} = $psectaddr; From e7d126f682249dd148ce50eb4a7b817e380c2aa5 Mon Sep 17 00:00:00 2001 From: AK6DN Date: Sun, 9 Apr 2017 19:41:18 -0700 Subject: [PATCH 13/19] Updated PSECT address/length processing --- obj2bin.pl | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/obj2bin.pl b/obj2bin.pl index 312567f..b9a63ee 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -774,16 +774,28 @@ sub parse_rec ($$$) { $psect{$nam}{FLG}{$flg&(1<<4) ? "R/O" : "R/W"}++; $psect{$nam}{FLG}{$flg&(1<<5) ? "REL" : "ABS"}++; $psect{$nam}{FLG}{$flg&(1<<7) ? "D" : "I/D"}++; - $val++ if $val & 1; - if ($psect{$nam}{FLG}{CON}) { - $psect{$nam}{LENGTH} = $val; - $psect{$nam}{START} = $psectaddr; - $psectname = $nam; - $psectaddr += $val; - } elsif ($psect{$nam}{FLG}{ABS}) { - $psect{$nam}{LENGTH} = $val; - $psect{$nam}{START} = 0; - $psectname = $nam; + $psectname = $nam; + if ($psect{$nam}{FLG}{ABS}) { + # absolute + if ($psect{$nam}{FLG}{CON}) { + # concatenated + warn sprintf("Warning: psect ABS,CON is not supported, psect='%s'\n", $psectname); + } else { + # overlaid + $psect{$nam}{LENGTH} = $val; + $psect{$nam}{START} = 0; + } + } else { + # relative + if ($psect{$nam}{FLG}{CON}) { + # concatenated + $psect{$nam}{LENGTH} = $val; + $psect{$nam}{START} = $psectaddr & 1 ? ++$psectaddr : $psectaddr; + $psectaddr += $val; + } else { + # overlaid + warn sprintf("Warning: psect REL,OVR is not supported, psect='%s'\n", $psectname); + } } } if ($DEBUG) { From 3d380827cbebf432260bdae75c718bb51a9e3b4a Mon Sep 17 00:00:00 2001 From: AK6DN Date: Sun, 9 Apr 2017 21:03:19 -0700 Subject: [PATCH 14/19] Minor update to psect flag processing --- obj2bin.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/obj2bin.pl b/obj2bin.pl index b9a63ee..4c6ba9f 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -780,19 +780,19 @@ sub parse_rec ($$$) { if ($psect{$nam}{FLG}{CON}) { # concatenated warn sprintf("Warning: psect ABS,CON is not supported, psect='%s'\n", $psectname); - } else { + } elsif ($psect{$nam}{FLG}{OVR}) { # overlaid $psect{$nam}{LENGTH} = $val; $psect{$nam}{START} = 0; } - } else { + } elsif ($psect{$nam}{FLG}{REL}) { # relative if ($psect{$nam}{FLG}{CON}) { # concatenated $psect{$nam}{LENGTH} = $val; $psect{$nam}{START} = $psectaddr & 1 ? ++$psectaddr : $psectaddr; $psectaddr += $val; - } else { + } elsif ($psect{$nam}{FLG}{OVR}) { # overlaid warn sprintf("Warning: psect REL,OVR is not supported, psect='%s'\n", $psectname); } From e3b33ef330421ab3b4a6ba1e3577a15bf766b890 Mon Sep 17 00:00:00 2001 From: AK6DN Date: Mon, 10 Apr 2017 00:17:00 -0700 Subject: [PATCH 15/19] Updated debug output for GSD psect processing --- obj2bin.pl | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/obj2bin.pl b/obj2bin.pl index 4c6ba9f..d501a91 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -738,7 +738,6 @@ sub parse_rec ($$$) { for (my $i = 2; $i < scalar(@$rec); ) { # GSD records are fixed 8B length all in the same format my $sym = &rad2asc(($rec->[$i+1]<<8)|($rec->[$i+0]<<0),($rec->[$i+3]<<8)|($rec->[$i+2]<<0)); - my $nam = &sym2psect($file,$sym); my $flg = $rec->[$i+4]; my $ent = $rec->[$i+5]; my $val = ($rec->[$i+7]<<8)|($rec->[$i+6]<<0); @@ -746,8 +745,12 @@ sub parse_rec ($$$) { my $def = undef; if ($ent == 3) { # XFRADR - $program{START}{PSECT} = $nam; + $program{START}{PSECT} = &sym2psect($file,$sym); $program{START}{VALUE} = $val; + if ($DEBUG) { + printf $LOG "..GSD: type='%-6s'(%03o) name='%s' value=%06o\n", + $ent[$ent], $ent, $program{START}{PSECT}, $program{START}{VALUE}; + } } elsif ($ent == 4) { # GBLSYM flags my $adr = $val + $psect{$psectname}{START}; @@ -765,9 +768,16 @@ sub parse_rec ($$$) { $gblsym{$sym}{$def}{VALUE} = $val; $gblsym{$sym}{$def}{ADDRESS} = $adr; } + if ($DEBUG) { + printf $LOG "..GSD: type='%-6s'(%03o) name='%s' value=%06o", $ent[$ent], $ent, $sym, $val; + printf $LOG " psect='%s' value=%06o", $gblsym{$sym}{$def}{PSECT}, $gblsym{$sym}{$def}{VALUE}; + printf $LOG " flags=%s\n", join(",", sort(keys(%{$gblsym{$sym}{$def}{FLG}}))); + } } elsif ($ent == 5) { # PSECT flags + my $nam = &sym2psect($file,$sym); $psect[++$psectnumb] = $nam; + $psect{$nam}{FILE} = $file; $psect{$nam}{NUMBER} = $psectnumb; $psect{$nam}{FLG}{$flg&(1<<0) ? "GBL" : $flg&(1<<6) ? "GBL" : "LCL"}++; $psect{$nam}{FLG}{$flg&(1<<2) ? "OVR" : "CON"}++; @@ -797,14 +807,11 @@ sub parse_rec ($$$) { warn sprintf("Warning: psect REL,OVR is not supported, psect='%s'\n", $psectname); } } - } - if ($DEBUG) { - printf $LOG "..GSD: type='%-6s'(%03o) name='%s' value=%06o", $ent[$ent], $ent, ($ent == 4 ? $sym : $nam), $val; - printf $LOG " psect='%s' value=%06o", $gblsym{$sym}{$def}{PSECT}, $gblsym{$sym}{$def}{VALUE} if $ent == 4; - printf $LOG " length=%06o start=%06o", $psect{$nam}{LENGTH}, $psect{$nam}{START} if $ent == 5; - printf $LOG " flags=%s", join(",", sort(keys(%{$gblsym{$sym}{$def}{FLG}}))) if $ent == 4; - printf $LOG " flags=%s", join(",", sort(keys(%{$psect{$nam}{FLG}}))) if $ent == 5; - printf $LOG "\n"; + if ($DEBUG) { + printf $LOG "..GSD: type='%-6s'(%03o) name='%s' value=%06o", $ent[$ent], $ent, $nam, $val; + printf $LOG " length=%06o start=%06o", $psect{$nam}{LENGTH}, $psect{$nam}{START}; + printf $LOG " flags=%s\n", join(",", sort(keys(%{$psect{$nam}{FLG}}))); + } } $i += 8; } From fd0eea148e27818274454b713b212fb9379f81da Mon Sep 17 00:00:00 2001 From: AK6DN Date: Wed, 19 Jul 2017 21:22:08 -0700 Subject: [PATCH 16/19] added .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a09a578 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# ignore these files: +# ignore these directories: +tests/ +# the end From fa38443f8a45c28e5f3176fee00cf21c4499aabb Mon Sep 17 00:00:00 2001 From: AK6DN Date: Mon, 25 Mar 2019 20:17:31 -0700 Subject: [PATCH 17/19] sort psect list in .log file in asc addr order instead of name order --- obj2bin.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obj2bin.pl b/obj2bin.pl index d501a91..54a57ab 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -822,7 +822,7 @@ sub parse_rec ($$$) { printf $LOG "..ENDGSD\n\n" if $DEBUG; $program{END}{ADDRESS} = 0; - foreach my $nam (sort(keys(%psect))) { + foreach my $nam (sort({$psect{$a}{START} == $psect{$b}{START} ? $psect{$a}{LENGTH} <=> $psect{$b}{LENGTH} : $psect{$a}{START} <=> $psect{$b}{START}} keys(%psect))) { my $start = $psect{$nam}{START}; my $length = $psect{$nam}{LENGTH}; my $end = $length ? $start + $length - 1 : $start; From 4523bd75e097e4f1d0a232d0598f8dce46829d6e Mon Sep 17 00:00:00 2001 From: AK6DN Date: Fri, 6 Mar 2020 21:51:29 -0800 Subject: [PATCH 18/19] Update documentation re: multiple input .obj files --- README.md | 10 +++------- obj2bin.pl | 9 +++------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e0de6d7..0f14b8f 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,6 @@ DESCRIPTION console load commands, and loadable absolute binary program images (.BIN) files. - Currently the program is limited to a single object input file that can be - output in the selected format. Multiple .psect/.asect ops are supported, - as well as all local (non-global) relocation directory entries. Multiple - object files are (not yet) supported. - OPTIONS The following options are available: @@ -128,6 +123,8 @@ EXAMPLES obj2bin.pl --verbose --binary --out memtest.bin memtest.obj + obj2bin.pl --verbose --binary --out prftst.bin prftst.obj mac/printf.obj + AUTHOR Don North - donorth @@ -140,7 +137,6 @@ HISTORY 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. - 2017-04-01 v2.0 donorth - Started to add capability to process multiple - input object files ... still a work in progress. + 2017-04-01 v2.0 donorth - Added capability to process multiple input object files. Renamed from obj2hex.pl to obj2bin.pl ``` diff --git a/obj2bin.pl b/obj2bin.pl index 54a57ab..21ec4ce 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -63,10 +63,6 @@ binary program images (.BIN) files. Multiple .psect/.asect ops are supported, as well as all local (non-global) relocation directory entries. -Multiple input object files are (not yet fully) supported - this -part is work in progress. In particular definition and resolution -of global symbols are not supported. - =head1 OPTIONS The following options are available: @@ -167,6 +163,8 @@ Some examples of common usage: obj2bin.pl --verbose --binary --out memtest.bin memtest.obj + obj2bin.pl --verbose --binary --out prftst.bin prftst.obj mac/printf.obj + =head1 AUTHOR Don North - donorth @@ -181,8 +179,7 @@ Modification history: 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. - 2017-04-01 v2.0 donorth - Started to add capability to process multiple - input object files ... still a work in progress. + 2017-04-01 v2.0 donorth - Added capability to process multiple input object files. Renamed from obj2hex.pl to obj2bin.pl =cut From 5015ea60c545a7a21b17d5f313601111b96fe1cf Mon Sep 17 00:00:00 2001 From: AK6DN Date: Fri, 6 Mar 2020 23:48:55 -0800 Subject: [PATCH 19/19] Update documentation comments --- README.md | 17 +++++++++-------- obj2bin.pl | 17 +++++++++-------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 0f14b8f..812df0f 100644 --- a/README.md +++ b/README.md @@ -131,12 +131,13 @@ AUTHOR HISTORY Modification history: - 2005-05-05 v1.0 donorth - Initial version. - 2016-01-15 v1.1 donorth - Added RLD(IR) processing, moved sub's to end. - 2016-01-18 v1.2 donorth - Added GSD processing, improved debug output. - 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. - 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. - 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. - 2017-04-01 v2.0 donorth - Added capability to process multiple input object files. - Renamed from obj2hex.pl to obj2bin.pl + 2005-05-05 v1.0 donorth - Initial version. + 2016-01-15 v1.1 donorth - Added RLD(IR) processing, moved sub's to end. + 2016-01-18 v1.2 donorth - Added GSD processing, improved debug output. + 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. + 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. + 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. + 2017-04-01 v1.9 donorth - Renamed from obj2hex.pl to obj2bin.pl + 2017-05-04 v1.95 donorth - Updated capability to read multiple input .obj files. + 2020-03-06 v2.0 donorth - Updated help documentation and README.md file. ``` diff --git a/obj2bin.pl b/obj2bin.pl index 21ec4ce..4eed710 100644 --- a/obj2bin.pl +++ b/obj2bin.pl @@ -173,14 +173,15 @@ Don North - donorth Modification history: - 2005-05-05 v1.0 donorth - Initial version. - 2016-01-15 v1.1 donorth - Added RLD(IR) processing, moved sub's to end. - 2016-01-18 v1.2 donorth - Added GSD processing, improved debug output. - 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. - 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. - 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. - 2017-04-01 v2.0 donorth - Added capability to process multiple input object files. - Renamed from obj2hex.pl to obj2bin.pl + 2005-05-05 v1.0 donorth - Initial version. + 2016-01-15 v1.1 donorth - Added RLD(IR) processing, moved sub's to end. + 2016-01-18 v1.2 donorth - Added GSD processing, improved debug output. + 2016-01-20 v1.3 donorth - Initial support for linking multiple PSECTs. + 2016-01-22 v1.4 donorth - Added objfile/outfile/logfile switches vs stdio. + 2016-01-28 v1.5 donorth - Added RLD processing, especially complex. + 2017-04-01 v1.9 donorth - Renamed from obj2hex.pl to obj2bin.pl + 2017-05-04 v1.95 donorth - Updated capability to read multiple input .obj files. + 2020-03-06 v2.0 donorth - Updated help documentation and README.md file. =cut