#!/usr/bin/env perl # Copyright (c) 2005 Kanru Chen # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. use strict; my @lineCache; my %symtable; my $line = 0; my $loc = 0; my $start = 0; my $plength; my %ins = ( ADD => {code => 0x18, type => 3}, ADDF => {code => 0x58, type => 3}, ADDR => {code => 0x90, type => 2}, AND => {code => 0x40, type => 3}, CLEAR => {code => 0xB4, type => 2}, COMP => {code => 0x28, type => 3}, COMPF => {code => 0x88, type => 3}, COMPR => {code => 0xA0, type => 2}, DIV => {code => 0x24, type => 3}, DIVF => {code => 0x64, type => 3}, DIVR => {code => 0x9C, type => 2}, FIX => {code => 0xC4, type => 1}, FLOAT => {code => 0xC0, type => 1}, HIO => {code => 0xF4, type => 1}, J => {code => 0x3C, type => 3}, JEQ => {code => 0x30, type => 3}, JGT => {code => 0x34, type => 3}, JLT => {code => 0x38, type => 3}, JSUB => {code => 0x48, type => 3}, LDA => {code => 0x00, type => 3}, LDB => {code => 0x68, type => 3}, LDCH => {code => 0x50, type => 3}, LDF => {code => 0x70, type => 3}, LDL => {code => 0x08, type => 3}, LDS => {code => 0x6C, type => 3}, LDT => {code => 0x74, type => 3}, LDX => {code => 0x04, type => 3}, LPS => {code => 0xD0, type => 3}, MUL => {code => 0x20, type => 3}, MULF => {code => 0x60, type => 3}, MULR => {code => 0x98, type => 2}, NORM => {code => 0xC8, type => 1}, OR => {code => 0x44, type => 3}, RD => {code => 0xD8, type => 3}, RMO => {code => 0xAC, type => 2}, RSUB => {code => 0x4C, type => 3}, SHIFTL => {code => 0xA4, type => 2}, SHIFTR => {code => 0xA8, type => 2}, SIO => {code => 0xF0, type => 1}, SSK => {code => 0xEC, type => 3}, STA => {code => 0x0C, type => 3}, STB => {code => 0x78, type => 3}, STCH => {code => 0x54, type => 3}, STF => {code => 0x80, type => 3}, STI => {code => 0xD4, type => 3}, STL => {code => 0x14, type => 3}, STS => {code => 0x7C, type => 3}, STSW => {code => 0xE8, type => 3}, STT => {code => 0x84, type => 3}, STX => {code => 0x10, type => 3}, SUB => {code => 0x1C, type => 3}, SUBR => {code => 0x94, type => 2}, SVC => {code => 0xB0, type => 2}, TD => {code => 0xE0, type => 3}, TIO => {code => 0xF8, type => 1}, TIX => {code => 0x2C, type => 3}, TIXR => {code => 0xB8, type => 2}, WD => {code => 0xDC, type => 3}, ); sub readline { $line++; chomp; return if /^\s*$/; return if /^\s*\./; m/(^\w*)\s+(.*)$/; my $label = $1; my $other = $2; &error("*** missing instruction ***\n") if !defined $other; my @st = split /\s+/, $other; if($other =~ /^.*c'(.*)'/) { $st[1] = "c'$1'"; } return ($label, $st[0], $st[1]); } sub error { my $err = shift; print STDERR "At line $line\n"; print STDERR "$err"; exit 1; } sub pass1 { my $f = shift; while(<$f>) { my @st = &readline; if(@st) { if ($line == 1 && $st[1] eq "START") { # First line $loc = hex($st[2]); $start = $loc; push @lineCache, {loc => $loc, label => $st[0], opcode => $st[1], operand => defined $st[2] ? $st[2] : ""}; next; } # Otherwise, record the line information. push @lineCache, {loc => $loc, label => $st[0], opcode => $st[1], operand => defined $st[2] ? $st[2] : ""}; if ($st[0] ne "") { # If have label. if (defined $symtable{$st[0]}) { # Already defined this label. &error("*** duplicate symbol ***\nPrevious defined at line $symtable{$st[0]}{first}\n"); } else { $symtable{$st[0]} = { loc => $loc, first => $line }; } } last if $st[1] eq "END"; if ($st[1] =~ s/\+(.*)/$1/) { # SIC/XE instruction # Format 4 if (exists $ins{$st[1]} and $ins{$st[1]}{type} == 3) { $loc += 4; } else { &error("*** wrong type of opcode ***\n"); } } elsif (exists $ins{$st[1]}) { $loc += $ins{$st[1]}{type}; } elsif ($st[1] eq "WORD") { $loc += 3; } elsif ($st[1] eq "RESW") { &error("*** missing operand ***\n") if !defined $st[2]; $loc += 3 * $st[2]; } elsif ($st[1] eq "RESB") { &error("*** missing operand ***\n") if !defined $st[2]; &error("*** operand need to be number ***\n") if !($st[2] =~ /^[0-9]+$/); $loc += $st[2]; } elsif ($st[1] eq "BYTE") { &error("*** missing operand ***\n") if !defined $st[2]; if ($st[2] =~ m/[xX]'(\w+)'/) { $loc += (length $1) % 2 == 0 ? (length $1) / 2 : &error("*** wrong byte number ***\n"); } elsif ($st[2] =~ m/[cC]'(.*)'/) { $loc += length $1; } else { &error("*** unknow operand $st[2] ***\n"); } } elsif ($st[1] eq "BASE") { } else { &error("*** unknow instruction ***\n"); } } } $plength = $loc - $start; } sub dump { for my $k (sort {$symtable{$a}{loc} cmp $symtable{$b}{loc}} keys %symtable) { printf "%04X %s\n", $symtable{$k}{loc}, $k; } printf "%X\n", $loc; } sub dumpObj { my @record; my %text; my ($isindex, $indirect, $immediate, $base); $text{len} = 0; for my $s (@lineCache) { ($isindex, $indirect, $immediate) = (0, 0, 0); $isindex = 1 if $s->{operand} =~ s/(.*?),[xX]/$1/; $indirect = 1 if $s->{operand} =~ s/@(.*)/$1/; $immediate = 1 if $s->{operand} =~ s/#(.*)/$1/; if ($s->{opcode} eq "START") { # First line. push @record, sprintf "H%-6s%06X%06X", $s->{label}, $start, $plength; next; } $text{loc} = sprintf ("T%06X", $s->{loc}) if $text{len} == 0; if ($s->{opcode} ne "END") { if ($s->{opcode} =~ s/\+(.*)/$1/) { # Format 4 if (exists $ins{$s->{opcode}}) { my $addr = 0; my $code = $ins{$s->{opcode}}{code}; if (exists $symtable{$s->{operand}}) { $addr = $symtable{$s->{operand}}{loc}; } else { $addr = $s->{operand}; } $addr |= 0x800000 if $isindex; $addr |= 0x100000; #Extended, format 4 $code |= 0x03; $code ^= 0x01 if $indirect; $code ^= 0x02 if $immediate; $text{obj} .= sprintf "%02X%06X", $code, $addr; $text{len} += 4; } } elsif (exists $ins{$s->{opcode}} and $ins{$s->{opcode}}{type} == 1) { # Format 1 $text{obj} .= sprintf "%02X", $ins{$s->{opcode}}{code}; $text{len} += 1; } elsif (exists $ins{$s->{opcode}} and $ins{$s->{opcode}}{type} == 2) { # Format 2 if ($s->{operand} =~ /([AXLBST])(,([AXLBST]))?/) { my %t = (A => 0, X => 1, L => 2, B => 3, S => 4, T => 5); my ($a, $b) = (0, 0); $a = $t{$1}; $b = $t{$3} if defined $3; $text{obj} .= sprintf "%02X%X%X", $ins{$s->{opcode}}{code}, $a, $b; $text{len} += 2; } else { $line = "$s->{opcode} $s->{operand}"; &error("*** Unsupported Register ***\n"); } } elsif ($s->{opcode} eq "RSUB") { $text{obj} .= sprintf "%02X%04X", $ins{$s->{opcode}}{code} | 0x3, 0; $text{len} += 3; } elsif (exists $ins{$s->{opcode}}) { # Format 3 my $mode = 0; my $addr = 0; my $code = $ins{$s->{opcode}}{code}; if (exists $symtable{$s->{operand}}) { $addr = $symtable{$s->{operand}}{loc}; } else { $addr = $s->{operand}; } unless ($addr eq $s->{operand}) { my $pc = $addr - $s->{loc} - 3; if ($pc >= -2048 and $pc <= 2047) { $addr = $pc; $mode |= 0x2; } elsif ($base) { my $b = $addr - $base; if ($b >= 0) { $addr = $b; $mode |= 0x4; } else { $line = "$s->{opcode} $s->{operand}"; &error("*** Please Use Format 4 Instead\n"); } } else { $line = "$s->{opcode} $s->{operand}"; &error("*** Please Use Format 4 Instead\n"); } } $mode |= 0x8 if $isindex; $code |= 0x03; $code ^= 0x01 if $indirect; $code ^= 0x02 if $immediate; if ($addr < 0) { $addr = substr(sprintf("%X", $addr), 5); } else { $addr = sprintf("%03X", $addr); } $text{obj} .= sprintf "%02X%X%s", $code, $mode, $addr; $text{len} += 3; } elsif ($s->{opcode} eq "BYTE") { if ($s->{operand} =~ m/[xX]'(\w+)'/) { $text{obj} .= $1; $text{len} += (length $1) / 2; } elsif ($s->{operand} =~ m/[cC]'(.*)'/) { $text{len} += length $1; my @t = split //, $1; for my $ch (@t) { $text{obj} .= sprintf "%02X", ord($ch); } } } elsif ($s->{opcode} eq "WORD") { $text{obj} .= sprintf "%06X", $s->{operand}; $text{len} += 3; } elsif ($s->{opcode} =~ /(RESW|RESB)/) { push @record, sprintf "$text{loc}%02X$text{obj}", $text{len} if $text{len} != 0; $text{len} = 0; $text{obj} = ""; } elsif ($s->{opcode} eq "BASE") { $base = $symtable{$s->{operand}}{loc}; } if ($text{len} >= 30) { push @record, sprintf "$text{loc}%02X$text{obj}", $text{len}; $text{len} = 0; $text{obj} = ""; } } else { push @record, sprintf "$text{loc}%02X$text{obj}", $text{len} if $text{len} != 0; push @record, sprintf "E%06X", $symtable{$s->{operand}}{loc}; } } for my $r (@record) { print "$r\n"; } } sub main { my $file; if (defined $ARGV[0]) { $file = $ARGV[0]; } else { $file = "SRCFILE"; } open my $f, "<$file" or die "Can't open file $file: $!\n"; &pass1($f); #&dump; &dumpObj; } &main;