I've restructured the opr code and the add code using the SimH logic

and I fixed up some bugs with the new command interpreter. I'm getting
so close to cat.s working!
This commit is contained in:
Warren Toomey
2016-03-01 16:55:03 +10:00
parent 8109d37e6d
commit 73c066ae9d
+146 -88
View File
@@ -27,6 +27,8 @@ use constant MAXPOSINT => 0377777; # Biggest signed integer
use constant MAXADDR => 017777; # Largest memory address
use constant LINKMASK => 01000000; # Mask for LINK register
use constant EAESTEP => 077; # EAE step count mask
use constant EAEIMASK => 0777700; # EAE instruction mask
use constant SIGN => 0400000; # Sign bit
### Main program ###
@@ -40,7 +42,7 @@ while ( defined( $ARGV[0] ) && ( $ARGV[0] =~ m{^-} ) ) {
# -b: set a breakpoint
if ( $ARGV[0] eq "-b" ) {
$singlestep = 1; shift(@ARGV);
shift(@ARGV);
$Breakpoint{ oct( shift(@ARGV) ) } = 1;
}
}
@@ -166,8 +168,7 @@ sub simulate {
oct("054") => \&sad,
oct("060") => \&jmp,
oct("064") => \&eae,
oct("070") => \&iot,
oct("074") => \&special,
oct("074") => \&opr,
);
# Loop indefinitely
@@ -183,7 +184,10 @@ sub simulate {
my $indaddr = ($indirect) ? $Mem[$addr] & MAXADDR : $addr;
# If this is a breakpoint, stop now and get a user command
$singlestep = 1 if ( defined( $Breakpoint{$PC} ) );
if ( defined( $Breakpoint{$PC} ) ) {
$singlestep = 1;
dprintf( "break at PC %06o\n", $PC );
}
get_user_command() if ($singlestep);
dprintf( "PC %06o: ", $PC );
@@ -194,7 +198,7 @@ sub simulate {
if ( defined( $Oplist{$opcode} ) ) {
$Oplist{$opcode}->( $instruction, $addr, $indaddr );
} else {
printf( STDERR "Unknown instruction 0%o at location 0%o\n",
printf( STDERR "Unknown instruction 0%06o at location 0%06o\n",
$instruction, $PC );
exit(1);
}
@@ -243,14 +247,26 @@ sub tad {
sub add {
my ( $instruction, $addr, $indaddr ) = @_;
dprintf( "add AC (value %06o) with addr %06o (%06o)\n",
$PC, $AC, $indaddr, $Mem[$indaddr] );
$LINK = 0;
$AC = $AC + $Mem[$indaddr];
if ( $AC & LINKMASK ) {
$AC++; # End-around carry
$LINK = LINKMASK;
$AC, $indaddr, $Mem[$indaddr] );
# $LINK = 0;
# $AC = $AC + $Mem[$indaddr];
# if ( $AC & LINKMASK ) {
# $AC++; # End-around carry
# $LINK = LINKMASK;
# }
# $AC = $AC & MAXINT;
#
# This logic shamelessly borrowed from SimH
# https://github.com/simh/simh/blob/master/PDP18B/pdp18b_cpu.c
my $sum= $AC + $Mem[$indaddr];
if ($sum > MAXINT) { # end around carry
$sum = ($sum + 1) & MAXINT;
}
$AC = $AC & MAXINT;
if (((~$AC ^ $sum) & ($AC ^ $sum)) & SIGN) { # overflow?
$LINK= LINKMASK; # set link
}
$AC= $sum;
$PC++;
}
@@ -290,7 +306,7 @@ sub dzm {
# Index and skip if zero
sub isz {
my ( $instruction, $addr, $indaddr ) = @_;
dprintf( "isz %06o\n", $Mem[$indaddr] );
dprintf( "isz %06o (value %06o)\n", $indaddr, $Mem[$indaddr] );
$Mem[$indaddr]++;
$Mem[$indaddr] &= MAXINT;
$PC += ( $Mem[$indaddr] == 0 ) ? 2 : 1;
@@ -313,79 +329,117 @@ sub jms {
$PC = $indaddr;
}
# Special instructions
sub special {
my $instruction = shift;
# OPR instructions
sub opr {
my ( $instruction, $addr, $indaddr ) = @_;
# Deal with each one in turn
# hlt
# hlt: halt simulation
if ( $instruction == 0740040 ) {
printf( STDERR "PC %06o: program halted\n", $PC );
dump_memory( 0, MAXADDR, 0 ) if ($debug);
exit(1);
}
if ( $instruction == 0741100 ) { # spa: skip on positive AC
dprintf( "spa AC %06o\n", $AC );
# Because we are dealing with 18 bits, compare the range
$PC += ( ( $AC >= 0 ) && ( $AC <= MAXPOSINT ) ) ? 2 : 1;
return;
}
if ( $instruction == 0741200 ) { # sna: skip on non-zero AC
dprintf( "sna AC %06o\n", $AC );
$PC += ( $AC != 0 ) ? 2 : 1;
return;
}
if ( $instruction == 0740200 ) { # sza: skip on zero AC
dprintf( "sza AC %06o\n", $AC );
$PC += ( $AC == 0 ) ? 2 : 1;
return;
}
if ( $instruction == 0741400 ) { # szl: Skip when $LINK is zero
dprintf( "szl LINK %0o\n", $LINK );
$PC += ( $LINK == 0 ) ? 2 : 1;
return;
}
if ( $instruction == 0740400 ) { # snl: Skip when $LINK not zero
dprintf( "snl LINK %0o\n", $LINK );
$PC += ( $LINK != 0 ) ? 2 : 1;
return;
}
if ( $instruction == 0741000 ) { # ska: skip always
dprintf("skp\n");
$PC += 2;
return;
}
# ral: rotate left or rcr: clear link then rotate left
if ( ( $instruction == 0740010 ) || ( $instruction == 0744010 ) ) {
$LINK = 0 if ( $instruction == 0744010 );
$AC = $AC << 1 + ($LINK) ? 1 : 0;
$LINK = $AC & LINKMASK;
$AC = $AC & MAXINT;
$PC++;
return;
}
# rar: rotate right or rcr: clear link then rotate right
if ( ( $instruction == 0740020 ) || ( $instruction == 0744020 ) ) {
$LINK = 0 if ( $instruction == 0744020 );
my $newlink = ( $AC & 1 ) ? LINKMASK : 0;
$AC = ( $LINK | $AC ) >> 1;
$LINK = $newlink;
$PC++;
return;
}
# law: load word into AC
if ( ( $instruction >= 0760000 ) && ( $instruction <= MAXINT ) ) {
my $indirect = ( $instruction >> 13 ) & 1;
if ( $indirect) {
dprintf( "law %06o into AC\n", $instruction );
$AC = $instruction;
$PC++;
return;
}
printf( STDERR "PC %06o: unknown instruction %06o\n", $PC, $instruction );
exit(1);
# List of skip opcode names for the next section
my @skipop= ( '', 'sma', 'sza', 'sza sma',
'snl', 'snl sma', 'snl sza', 'snl sza sma',
'skp', 'spa', 'sna', 'sna spa',
'szl', 'szl spa', 'szl sna', 'szl sna spa');
# This logic shamelessly borrowed from SimH
# https://github.com/simh/simh/blob/master/PDP18B/pdp18b_cpu.c
my $skip=0;
my $i= ($instruction >> 6) & 017; # decode IR<8:11>
dprintf("L.AC %d.%06o %s", ($LINK) ? 1 : 0, $AC, $skipop[$i]);
$skip=1 if (($i == 1) && ($AC & SIGN) != 0); # sma
$skip=1 if (($i == 2) && ($AC & MAXINT) == 0); # sza
$skip=1 if (($i == 3) &&
((($AC & MAXINT) == 0) || (($AC & SIGN) != 0))); # sza | sma
$skip=1 if (($i == 4) && ($LINK)); # snl
$skip=1 if (($i == 5) && ($LINK || ($AC >= SIGN))); # snl | sma
$skip=1 if (($i == 6) && ($LINK || ($AC == 0))); # snl | sza
$skip=1 if (($i == 7) &&
($LINK || ($AC >= SIGN) || ($AC == 0))); # snl | sza | sma
$skip=1 if ($i == 010); # skp
$skip=1 if (($i == 011) && (($AC & SIGN) == 0)); # spa
$skip=1 if (($i == 012) && (($AC & MAXINT) != 0)); # sna
$skip=1 if (($i == 013) &&
(($AC & MAXINT) != 0) && (($AC & SIGN) == 0)); # sna & spa
$skip=1 if (($i == 014) && ($LINK == 0)); # szl
$skip=1 if (($i == 015) && ($LINK == 0) && ($AC < SIGN)); # szl & spa
$skip=1 if (($i == 016) && ($LINK == 0) && ($AC != 0)); # szl & sna
$skip=1 if (($i == 017) &&
($LINK == 0) && ($AC != 0) && ($AC != 0)); # szl & sna & spa
# Clear operations
if ($instruction & 010000) { # cla
dprintf(" cla"); $AC=0;
}
if ($instruction & 004000) { # cli
dprintf(" cli"); $LINK=0;
}
if ($instruction & 000002) { # cmi
dprintf(" cmi"); $LINK= ($LINK) ? 0 : LINKMASK;
}
if ($instruction & 000001) { # cma
dprintf(" cma"); $AC= ($AC ^ MAXINT) & MAXINT;
}
# Rotate instructions
$i= $instruction & 02030;
# Single rotate right
if ($i == 020) {
dprintf(" rar");
my $newlink = ( $AC & 1 ) ? LINKMASK : 0;
$AC = ( $LINK | $AC ) >> 1;
$LINK = $newlink;
}
# Double rotate right
if ($i == 02020) {
dprintf(" rtr");
my $msb= ($AC & 1) << 17;
my $newlink = ( $AC & 2 ) ? LINKMASK : 0;
$AC = (( $LINK | $AC ) >> 2) | $msb;
$LINK = $newlink;
}
# Single rotate left
if ($i == 010) {
dprintf(" ral");
my $newlink = ( $AC & SIGN ) ? LINKMASK : 0;
my $lsb = $LINK ? 1 : 0;
$AC= (($AC << 1) | $lsb) & MAXINT;
$LINK = $newlink;
}
# Double rotate left
if ($i == 02010) {
dprintf(" rtl");
my $newlink = ( $AC & 0200000 ) ? LINKMASK : 0;
my $lsb = ( $AC & SIGN ) ? 1 : 0;
my $twolsb = $LINK ? 2 : 0;
$AC= (($AC << 2) | $twolsb | $lsb) & MAXINT;
$LINK = $newlink;
}
# Impossible left and right rotates: 02030 or 00030. Do nothing!
# Note: We didn't do the oas instruction above.
$PC+= 1 + $skip;
dprintf("\n");
return;
}
# cal: used for system calls
@@ -414,25 +468,25 @@ sub cal {
sub eae {
my ( $instruction, $addr, $indaddr ) = @_;
my $step = $instruction & EAESTEP;
$instruction &= EAEIMASK;
if ( $instruction == 0660500 ) { # lrss: long right shift, signed
# We ignore the MQ as it's not
# used by any user-mode programs
dprintf( "lrss %06o AC step %d\n", $AC, $step );
# Save the AC's sign into LINK
$LINK = ( $AC << 1 ) & LINKMASK;
my $newlink = ( $AC << 1 ) & LINKMASK;
# XXX: Do we need to preserve the AC sign?
$AC = $AC >> $step;
$AC = (($LINK |$AC) >> $step) & MAXINT;
$LINK= $newlink;
$PC++;
return;
}
if ( $instruction == 0660711 ) { # alss: long left shift, signed
if ( $instruction == 0660700 ) { # alss: long left shift, signed
# We don't fill the lsb with LINK yet
dprintf( "alss %06o AC step %d\n", $AC, $step );
# Save the AC's sign into LINK
$LINK = ( $AC << 1 ) & LINKMASK;
# XXX: Do we need to preserve the AC sign?
$AC = ( $AC << $step ) & MAXINT;
$PC++;
return;
@@ -480,17 +534,21 @@ sub sys_open {
# Get the start address of the string
my $start = $Mem[ $PC + 1 ];
# Convert this to a sensible ASCII filename
my $filename = mem2arg($start);
# Choose to open read-only or write-only
my $readorwrite= ($Mem[ $PC + 2 ]) ? ">" : "<";
dprintf( "open: base %06o, %s file %s\n", $start, $readorwrite, $filename );
# Bump up the PC
$PC += 3;
# Convert this to a sensible ASCII filename
my $filename = mem2arg($start);
dprintf( "open: base %06o, file %s\n", $start, $filename );
# Open the file
if ( open( my $FH, "<", $filename ) ) {
if ( open( my $FH, $readorwrite, $filename ) ) {
# Find a place in the @FD array to store this filehandle. 99 is arbitrary
# Find a place in the @FD array to store this filehandle.
# 99 is arbitrary
foreach my $fd ( 0 .. 99 ) {
if ( !defined( $FD[$fd] ) ) {
$FD[$fd] = $FH;
@@ -573,7 +631,7 @@ sub sys_write {
my $end = ( $start + $count - 1 ) & MAXADDR;
die("sys_write: bad start/end addresses $start $end\n")
if ( $end < $start );
dprintf( "write: %d words from %o to fd %d\n", $count, $start, $fd );
dprintf( "write: %d words from %06o to fd %d\n", $count, $start, $fd );
# Bump up the PC
$PC += 3;