PDP-8-E-Simulator/Utilities/Disassembler.m

282 lines
8.6 KiB
Objective-C

/*
* PDP-8/E Simulator
*
* Copyright © 1994-2015 Bernhard Baehr
*
* Disassembler.m - Disassembler for PDP-8/E instructions
*
* This file is part of PDP-8/E Simulator.
*
* PDP-8/E Simulator is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Cocoa/Cocoa.h>
#import "Disassembler.h"
#import "Unicode.h"
#import "PDP8.h"
@implementation Disassembler
+ (Disassembler *) sharedDisassembler
{
static Disassembler *sharedDisassembler;
if (! sharedDisassembler)
sharedDisassembler = [[self alloc] init];
return sharedDisassembler;
}
- (id) init
{
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:@"disassembler" ofType:@"plist"]];
self = [super init];
mriOpcodes = [[dict objectForKey:@"MRI"] retain];
iotOpcodes = [[dict objectForKey:@"IOT"] retain];
oprGroup1Opcodes = [[dict objectForKey:@"OPR Group 1"] retain];
oprGroup2Opcodes = [[dict objectForKey:@"OPR Group 2"] retain];
eaeModeAOpcodes = [[dict objectForKey:@"EAE Mode A"] retain];
eaeModeBOpcodes = [[dict objectForKey:@"EAE Mode B"] retain];
return self;
}
- (NSString *) disassembleOpcodeForPDP8:(PDP8 *)pdp8
atAddress:(int)addr showOperandsAtPC:(BOOL)showOpAtPC
{
short inst, word1, word2, ad, d, e, pc, field;
char str[128], *p;
NSCAssert1 ((addr & ~077777) == 0, @"Bad address %o", addr);
if (addr >= [pdp8 memorySize])
return NSLocalizedString(@"n/a", @"");
p = str;
pc = [pdp8 getProgramCounter];
inst = [pdp8 memoryAt:addr];
word1 = [pdp8 memoryAtNext:addr];
switch (inst & 07000) {
case 00000 : /* AND */
case 01000 : /* TAD */
case 02000 : /* ISZ */
case 03000 : /* DCA */
case 04000 : /* JMS */
case 05000 : /* JMP */
[[mriOpcodes objectAtIndex:(inst >> 9)]
getCString:p maxLength:(sizeof(str) - 1) encoding:NSASCIIStringEncoding];
p += strlen(p);
p += sprintf(p, " %s", (inst & 0400) ? "I " : "");
ad = ((inst & 0200) ? (addr & 07600) : 0) | (inst & 0177);
d = ad - (addr & 07777);
if (inst >= 04000 && ! (inst & 0400)) {
if (d == 0)
p += sprintf(p, ".");
else if (-5 < d && d < 5)
p += sprintf(p, ".%+d" /* %d (not %o) to get sign */, d);
else
p += sprintf(p, "%o", ad);
} else
p += sprintf(p, "%o", ad);
if (addr == pc && showOpAtPC) {
field = ((inst >= 04000 && ! (inst & 0400) && [pdp8 getIB] != [pdp8 getIF]) ?
[pdp8 getIB] : [pdp8 getIF]) << 12;
d = ((inst & 0400) && ad > 07 && ad < 020) ? 1 : 0;
/* indirect auto index register access */
word1 = field | ad;
ad = ([pdp8 memoryAt:(field | ad)] + d) & 07777;
if (inst < 05000 || inst >= 05400)
p += sprintf(p, " (%4.4o)", ad);
if (inst < 05000 && (inst & 0400)) {
field = ((inst < 04000) ? [pdp8 getDF] : [pdp8 getIB]) << 12;
d = (d && word1 == (field | ad)) ? 1 : 0;
ad = ([pdp8 memoryAt:(field | ad)] + d) & 07777;
p += sprintf(p, " ((%4.4o))", ad);
}
}
break;
case 06000 : /* IOT */
[[iotOpcodes objectAtIndex:(inst & 0777)]
getCString:p maxLength:sizeof(str) encoding:NSASCIIStringEncoding];
p += strlen(p);
break;
case 07000 : /* OPR */
switch (inst & 07401) {
case 07000 : /* OPR Group I */
case 07001 :
[[oprGroup1Opcodes objectAtIndex:(inst & 0377)]
getCString:p maxLength:sizeof(str) encoding:NSASCIIStringEncoding];
p += strlen(p);
break;
case 07400 : /* OPR Group II */
[[oprGroup2Opcodes objectAtIndex:((inst & 0377) >> 1)]
getCString:p maxLength:sizeof(str) encoding:NSASCIIStringEncoding];
p += strlen(p);
break;
/*
* | | | |
* |---|---|---|---|---|---|---|---|---|
* | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11|
* | 1 |CLA|MQA|SCA|MQL| | | | 1 | Group III Mode A
* |---|---|---|---|---|---|---|---|---|
* Sequence: 1 2 2 \____3____/
* V
* 0 = NOP 2 = MUY# 4 = NMI 6 = ASR#
* 1 = SCL# 3 = DVI# 5 = SHL# 7 = LSR#
*
* | | | |
* |---|---|---|---|---|---|---|---|---|
* | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11|
* | 1 |CLA|MQA| |MQL| | | | 1 | Group III Mode B
* |---|---|---|---|---|---|---|---|---|
* Sequence: 1 2 \ 2 /
* \_______3_______/
* V
* 0 = NOP 4 = NMI 10 = SCA 14 = DPSZ
* 1 = ACS 5 = SHL# 11 = DAD# 15 = DPIC*
* 2 = MUY# 6 = ASR# 12 = DST# 16 = DCM*
* 3 = DVI# 7 = LSR# 13 = SWBA 17 = SAM
* # = 2-word instructions
* * = (MQL & MQA must be set)
*/
case 07401 : /* EAE */
d = (inst & 0377) >> 1 ;
if ([pdp8 getEAEmode] == EAE_MODE_A) {
[[eaeModeAOpcodes objectAtIndex:d]
getCString:p maxLength:sizeof(str) encoding:NSASCIIStringEncoding];
p += strlen(p);
switch (inst & 016) {
case 002 : /* SCL */
case 004 : /* MUY */
case 006 : /* DVI */
case 012 : /* SHL */
case 014 : /* ASR */
case 016 : /* LSR */
if (inst != 07447) { /* SWBA */
p += sprintf(p, " %o", word1);
/* word1 is operand */
}
break;
default :
break;
}
} else { /* EAE mode B */
[[eaeModeBOpcodes objectAtIndex:d]
getCString:p maxLength:sizeof(str) encoding:NSASCIIStringEncoding];
p += strlen(p);
switch (inst & 056) {
case 04 : /* MUY */
case 06 : /* DVI */
p += sprintf(p, " %o", word1);
/* DF|word1 points to operand word */
if (addr == pc && showOpAtPC) {
ad = (addr & 070000) | ((addr + 1) & 07777);
d = ((ad & 07770) == 010) ? 1 : 0; /* autoindex? */
word1 = ([pdp8 getDF] << 12) | ((word1 + d) & 07777);
d = (d && (ad == word1)) ? 1 : 0;
/* operand == autoindexed ptr? */
p += sprintf(p, " (%4.4o)",
([pdp8 memoryAt:word1] + d) & 07777);
}
break;
case 012 : /* SHL */
case 014 : /* ASR */
case 016 : /* LSR */
p += sprintf(p, " %o", word1);
/* word1 is operand */
break;
case 042 : /* DAD */
case 044 : /* DST */
p += sprintf(p, " %o", word1);
/* DF|word1 points to op double word */
/* DAD adds op double word to AC'MQ */
/* DST overwrites op dword with AC'MQ */
if (addr == pc && showOpAtPC) {
ad = (addr & 070000) | ((addr + 1) & 07777);
d = ((ad & 07770) == 010) ? 1 : 0; /* autoindex? */
word2 = ([pdp8 getDF] << 12) | ((word1 + d + 1) & 07777);
word1 = ([pdp8 getDF] << 12) | ((word1 + d) & 07777);
e = (d && (ad == word2)) ? 1 : 0;
/* operand == autoindexed ptr? */
d = (d && (ad == word1)) ? 1 : 0;
p += sprintf(p, " (%4.4o%4.4o)",
([pdp8 memoryAt:word2] + e) & 07777,
([pdp8 memoryAt:word1] + d) & 07777);
}
break;
}
}
break;
}
break;
}
*p = '\0'; /* to avoid Xcode analyzer warning */
return [NSString stringWithCString:str encoding:NSASCIIStringEncoding];
}
- (NSString *) operandInfoForPDP8:(PDP8 *)pdp8 atAddress:(int)addr
{
NSMutableString *string;
int inst, op, ad, oldad, field, d, d1, mark, i;
inst = [pdp8 memoryAt:addr];
if (inst >= 06000)
return nil; /* no MRI */
if (05000 <= inst && inst < 05400)
return nil; /* direct JMP */
ad = ((inst & 0200) ? (addr & 07600) : 0) | (inst & 0177);
d = ((inst & 0400) && ad > 07 && ad < 020) ? 1 : 0; /* indirect auto index register access */
ad |= addr & 070000;
op = ([pdp8 memoryAt:ad] + d) & 07777;
string = [NSMutableString stringWithFormat:@"%5.5o: %4.4o", ad, op];
if (inst < 05000 && (inst & 0400)) {
[string appendString:@"\n\n"];
oldad = ad;
ad = op;
mark = ((inst < 04000) ? [pdp8 getDF] : [pdp8 getIB]) << 12;
for (i = 0; i < 4; i++) {
field = i << 12;
part2 :
d1 = (d && oldad == (field | ad)) ? 1 : 0;
op = ([pdp8 memoryAt:field | ad] + d1) & 07777;
[string appendFormat:@"%5.5o: %4.4o", field | ad, op];
if (field == mark)
[string appendFormat:@"%C", UNICODE_DIAMOND];
if (field & 040000)
[string appendString:@"\n"];
else {
[string appendString:@"\t"];
field |= 040000;
goto part2;
}
}
}
return string;
}
- (void) addMnemonic:(NSString *)mnemonic forIOT:(int)opcode
{
NSAssert1 ((opcode & ~00777) == 06000, @"Invalid IOT %o", opcode);
[iotOpcodes replaceObjectAtIndex:opcode & 0777 withObject:mnemonic];
}
@end