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:
+146
-88
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user