/* * PDP-8/E Simulator * * Copyright © 1994-2015 Bernhard Baehr * * Assembler.h - Inline assembler 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 . */ #import #import "Assembler.h" #import "Opcode.h" @implementation Assembler #define isNothing 0 #define isNumber 1 #define isIOT 2 #define needsOperand 4 #define isMRI 8 #define isIndirect 16 #define isOPR1 32 #define isOPR2 64 #define isOPR3a 128 #define isOPR3b 256 #define isRelativeAddress 512 #define asmNoError nil #define asmBadNumber NSLocalizedString(@"Invalid octal number.", @"") #define asmUndefSymbol NSLocalizedString(@"Undefined opcode.", @"") #define asmBadAddress NSLocalizedString(@"Illegal address.", @"") #define asmBadRelativeAddress NSLocalizedString(@"Illegal relative address.", @"") #define asmCannotStartWithThis NSLocalizedString(@"Opcode expected.", @"") #define asmRelativeAddressNotAllowedHere NSLocalizedString(@"Relative address not allowed here.", @"") #define asmIndirectNotAllowedHere NSLocalizedString(@"Indirect addressing not allowed here.", @"") #define asmOffpageReference NSLocalizedString(@"Offpage reference.", @"") #define asmEndExpected NSLocalizedString(@"End of line expected.", @"") #define asmIllegalMicroprogramming NSLocalizedString(@"Invalid combination of opcodes.", @"") #define asmOperandNotAllowedHere NSLocalizedString(@"Operand not allowed here.", @"") #define asmBadFieldNumber NSLocalizedString(@"Bad field number.", @"") #define asmNeedsOperand NSLocalizedString(@"Operand expected.", @"") #define asmInputTooLong NSLocalizedString(@"Input too long.", @"") #define asmInvalidCharacters NSLocalizedString(@"Input contains invalid characters.", @"") + (Assembler *) sharedAssembler { static Assembler *sharedAssembler; if (! sharedAssembler) sharedAssembler = [[self alloc] init]; return sharedAssembler; } - (Assembler *) init { NSString *key, *flagOrOpcode; NSNumber *newFlag; NSEnumerator *arrayEnum; int opcode, flag, n; char buf[128]; NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"assembler" ofType:@"plist"]]; NSEnumerator *dictEnum = [dict keyEnumerator]; NSDictionary *flags = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:isMRI], @"isMRI", [NSNumber numberWithInt:isIOT], @"isIOT", [NSNumber numberWithInt:isOPR1], @"isOPR1", [NSNumber numberWithInt:isOPR2], @"isOPR2", [NSNumber numberWithInt:isOPR3a], @"isOPR3a", [NSNumber numberWithInt:isOPR3b], @"isOPR3b", [NSNumber numberWithInt:isIndirect], @"isIndirect", [NSNumber numberWithInt:needsOperand], @"needsOperand", nil]; self = [super init]; symtab = [[NSMutableDictionary alloc] init]; while ((key = [dictEnum nextObject])) { arrayEnum = [[dict valueForKey:key] objectEnumerator]; flag = 0; opcode = -1; while ((flagOrOpcode = [arrayEnum nextObject])) { newFlag = [flags objectForKey:flagOrOpcode]; if (newFlag) flag |= [newFlag intValue]; else { [flagOrOpcode getCString:buf maxLength:(sizeof(buf) - 1) encoding:NSMacOSRomanStringEncoding]; n = sscanf(buf, "%o", &opcode); NSAssert1 (n == 1 && (opcode & ~07777) == 0, @"Invalid parameter for opcode %@", key); if (n) ; // to avoid Analyzer warning "Value of n never used" } } NSAssert1 (flag != 0 && opcode != -1, @"Incomplete parameters for opcode %@", key); [symtab setObject:[NSNumber numberWithInt:((flag << 12) | opcode)] forKey:key]; } return self; } - (char *) lookupSymbol:(char *)s getOctal:(long *)octal getFlags:(ushort *)flags error:(NSString **)err { char *t, c; unsigned long n; NSNumber *sym; *err = asmNoError; while (*s == ' ') s++; if (*s == '\0') { *octal = 0; *flags = isNothing; return (s); } if (*s == '.') { s++; while (*s == ' ') s++; if (*s == '+' || *s == '-') { c = *s++; while (*s == ' ') s++; } else if (*s) { *err = asmBadRelativeAddress; return (s); } else { *flags = isRelativeAddress; *octal = 0; return (s); } } else c = '\0'; t = s; while ('0' <= *t && *t <= '7') t++; if (t > s) { t = s; for (n = 0; '0' <= *s && *s <= '7'; s++) { n = 8 * n + *s - '0'; if (n & ~07777) { *err = c ? asmBadRelativeAddress : asmBadNumber; return (t); } } if (c) { *flags = isRelativeAddress; *octal = (c == '+') ? n : -n; } else { *flags = isNumber; *octal = n; } return (s); } if (c) { *err = asmBadRelativeAddress; return (s); } while (*t && *t != ' ') t++; c = *t; *t = '\0'; sym = [symtab objectForKey:[NSString stringWithCString:s encoding:NSMacOSRomanStringEncoding]]; *t = c; if (sym == nil) { *err = (*s == '8' || *s == '9') ? asmBadNumber : asmUndefSymbol; t = s; } else { *octal = [sym intValue] & 07777; *flags = [sym intValue] >> 12; } return (t); } - (Opcode *) assemble:(NSString *)source atAddress:(int)address error:(NSString **)error { char buf[128]; char *bp, *oldbp; long w, word, word1; ushort cl, flags; int errpos = 0; NSString *err = asmNoError; Opcode *opcode = [Opcode opcodeWithAddress:address]; if (! [[source uppercaseString] getCString:buf maxLength:(sizeof(buf) - 1) encoding:NSMacOSRomanStringEncoding]) { err = [source canBeConvertedToEncoding:NSMacOSRomanStringEncoding] ? asmInputTooLong : asmInvalidCharacters; goto error; } bp = buf; word = 0; word1 = -1; flags = isNothing; for (;;) { oldbp = bp; bp = [self lookupSymbol:bp getOctal:&w getFlags:&cl error:&err]; while (*oldbp == ' ') oldbp++; errpos = oldbp - buf; if (err) goto error; if (cl == isNothing) break; if (flags == isNothing && (cl & (isIndirect | isRelativeAddress))) { err = asmCannotStartWithThis; goto error; } if (cl == isRelativeAddress) { if (! (flags & isMRI) || (word != 04000 && word != 05000)) { err = asmRelativeAddressNotAllowedHere; goto error; } cl = isNumber; w = (w + (long int) address) & 07777; } if (! (flags & isMRI) && (cl & isIndirect)) { err = asmIndirectNotAllowedHere; goto error; } if (flags & (isIndirect | isMRI)) { if (flags & cl & isIndirect) { err = asmIndirectNotAllowedHere; goto error; } if (cl == isNumber) { if (w < 0 || w > 07777) { err = asmBadAddress; goto error; } if (w >= 0200) { if ((w & 07600) != (address & 07600)) { err = asmOffpageReference; goto error; } w = 0200 | (w & 0177); } flags &= ~needsOperand; } else if (! (cl & isIndirect)) { err = asmIllegalMicroprogramming; goto error; } } if (flags & isIOT) { if (! (cl & (isIOT | isNumber))) { err = asmIllegalMicroprogramming; goto error; } if (cl == isNumber) { if (! (flags & needsOperand)) { err = asmOperandNotAllowedHere; goto error; } if (w < 010) w <<= 3; if (w & ~070) { err = asmBadFieldNumber; goto error; } flags &= ~needsOperand; } else if ((word & 07770) != (w & 07770)) { err = asmIllegalMicroprogramming; goto error; } } if (flags & (isOPR1 | isOPR2 | isOPR3a | isOPR3b)) { if (cl == isNumber) { if (! (flags & needsOperand)) { err = asmOperandNotAllowedHere; goto error; } flags &= ~needsOperand; word1 = w; w = 0; } else { if (! (cl & flags)) { err = asmIllegalMicroprogramming; goto error; } cl &= flags | needsOperand; } } if (flags & isNumber) { err = asmEndExpected; goto error; } flags |= cl; word |= w; } if (flags & needsOperand) { err = asmNeedsOperand; goto error; } [opcode setWord0:(word & 07777)]; if (word1 != -1) [opcode setWord1:(word1 & 07777)]; error : if (err) { if (error) *error = [NSString stringWithFormat:@"%d %@", errpos, err]; return nil; } return opcode; } - (void) addMnemonic:(NSString *)mnemonic forIOT:(int)opcode { NSAssert1 ((opcode & ~00777) == 06000, @"Invalid IOT %o", opcode); [symtab setObject:[NSNumber numberWithInt:((isIOT << 12) | opcode)] forKey:mnemonic]; } @end