/* * PDP-8/E Simulator * * Copyright © 1994-2015 Bernhard Baehr * * StateMachine.c - State Machine for the KC8-EA Programmer’s Console * * 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 "PluginFramework/PDP8.h" #import "PluginFramework/Utilities.h" #import "StateMachine.h" // these bits are in the positions of the "state" display #define FETCH 04000 #define DEFER 02000 #define EXECUTE 01000 #define NO_CYCLE 00000 // opcodes in the instruction register #define IR_AND 00000 #define IR_TAD 00100 #define IR_ISZ 00200 #define IR_DCA 00300 #define IR_JMS 00400 #define IR_JMP 00500 #define IR_IOT 00600 #define IR_OPR 00700 // values for mdDir: to or from memory #define MB_TO_MEM 00000 #define MB_FROM_MEM 00040 // coder keys #define CODER_KEY_CYCLE @"cycle" #define CODER_KEY_MD @"md" #define CODER_KEY_MDDIR @"mdDir" #define CODER_KEY_IR @"ir" #define CODER_KEY_CPMA @"cpma" #define CODER_KEY_BUS @"bus" #define CODER_KEY_PAUSE @"pause" #define CODER_KEY_AUTOINDEX @"autoindex" #define CODER_KEY_INTERRUPT_IN_PROGRESS @"interruptInProgress" #define CODER_KEY_OLDPC @"oldpc" #define CODER_KEY_OLDINST @"oldinst" @implementation StateMachine - (StateMachine *) initWithPDP8:(PDP8 *)p8 { self = [super init]; pdp8 = p8; [self updateState:NO]; return self; } - (StateMachine *) initWithCoder:(NSCoder *)coder pdp8:(PDP8 *)p8 { self = [self initWithCoder:coder]; pdp8 = p8; [self updateState:NO]; return self; } - (id) initWithCoder:(NSCoder *)coder { self = [super init]; cycle = [coder decodeIntForKey:CODER_KEY_CYCLE]; md = [coder decodeIntForKey:CODER_KEY_MD]; mdDir = [coder decodeIntForKey:CODER_KEY_MDDIR]; ir = [coder decodeIntForKey:CODER_KEY_IR]; cpma = [coder decodeIntForKey:CODER_KEY_CPMA]; bus = [coder decodeIntForKey:CODER_KEY_BUS]; pause = [coder decodeBoolForKey:CODER_KEY_PAUSE]; autoindex = [coder decodeIntForKey:CODER_KEY_AUTOINDEX]; interruptInProgress = [coder decodeBoolForKey:CODER_KEY_INTERRUPT_IN_PROGRESS]; oldpc = [coder decodeIntForKey:CODER_KEY_OLDPC]; oldinst = [coder decodeIntForKey:CODER_KEY_OLDINST]; return self; } - (void) encodeWithCoder:(NSCoder *)coder { [coder encodeInt:cycle forKey:CODER_KEY_CYCLE]; [coder encodeInt:md forKey:CODER_KEY_MD]; [coder encodeInt:mdDir forKey:CODER_KEY_MDDIR]; [coder encodeInt:ir forKey:CODER_KEY_IR]; [coder encodeInt:cpma forKey:CODER_KEY_CPMA]; [coder encodeInt:bus forKey:CODER_KEY_BUS]; [coder encodeBool:pause forKey:CODER_KEY_PAUSE]; [coder encodeInt:autoindex forKey:CODER_KEY_AUTOINDEX]; [coder encodeBool:interruptInProgress forKey:CODER_KEY_INTERRUPT_IN_PROGRESS]; [coder encodeInt:oldpc forKey:CODER_KEY_OLDPC]; [coder encodeInt:oldinst forKey:CODER_KEY_OLDINST]; } - (unsigned short) state:(BOOL)sw { return cycle | ir | mdDir | (sw ? 00010 : 0) | (pause ? 00004 : 0); } - (unsigned short) status { return ([pdp8 getL] ? 04000 : 0) // Link | ([pdp8 getGTF] ? 02000 : 0) // Greater Than Flag | ([pdp8 interruptRequest] ? 01000 : 0) // Interrupt Request | ([pdp8 getInhibit] || [pdp8 getDelay] ? 00400 : 0) // NO INT | ([pdp8 getEnable] ? 00200 : 0) // Interrupt On | (interruptInProgress ? 0 : [pdp8 getUF] ? 00100 : 0) // User Flag | ([pdp8 getIF] << 3) // Instruction Field | [pdp8 getDF]; // Data Field } - (unsigned short) md { return md; } - (unsigned short) bus { return bus; } - (unsigned short) cpma { return cpma; } - (BOOL) isInFetchCycle { return cycle == FETCH; } - (void) loadAddress { if ([pdp8 isStopped]) { cpma = ([pdp8 getIF] << 12) | [pdp8 getSR]; bus = [pdp8 getSR]; [pdp8 setPC:bus]; cycle = FETCH; } } - (void) loadExtendedAddress { if ([pdp8 isStopped]) { bus = [pdp8 getSR]; [pdp8 loadExtendedAddress]; } } - (void) setDatabus { bus = [pdp8 getAC]; if ((md & 07200) == 07200) // 7200 == CLA at seq. 1 bus = 0; if ((md & 07501) == 07501) // 7501 == MQA at seq. 2 bus |= [pdp8 getMQ]; if ((md & 07404) == 07404) // 7404 == OSR at seq. 3 bus |= [pdp8 getSR]; /* the bus setting may be wrong for group 3 OPRs implemented by the EAE */ } - (void) updateState:(BOOL)exitFromGo { autoindex = -1; if ([pdp8 isStopped]) { if ([pdp8 getProgramCounter] != oldpc || [pdp8 memoryAt:oldpc] != oldinst) { oldpc = cpma = [pdp8 getProgramCounter]; oldinst = [pdp8 memoryAt:oldpc]; cycle = FETCH; if (exitFromGo) { ir = ([pdp8 getCurrentOpcode] & 07000) >> 3; mdDir = MB_FROM_MEM; md = [pdp8 getCurrentOpcode]; [self setDatabus]; } } } else { /* set state at the end of the next instructions fetch cycle, before the instruction is executed */ bus = [pdp8 getAC]; if ([pdp8 getEnable] && [pdp8 interruptRequest] && ! ([pdp8 getDelay] || [pdp8 getInhibit])) { /* interrupt */ cpma = 0; ir = IR_JMS; cycle = EXECUTE; } else { /* normal instruction fetch */ md = [pdp8 memoryAt:[pdp8 getProgramCounter]]; mdDir = MB_FROM_MEM; ir = (md & 07000) >> 3; cpma = ([pdp8 getIF] << 12) | ((md & 00200) ? (cpma & 07600) : 0) | (md & 0177); if (ir <= IR_JMP && (md & 00400)) /* indirect MRI */ cycle = DEFER; else if (ir < IR_JMP) /* direct MRI != JMP */ cycle = EXECUTE; else { /* OPR, IOT or direct JMP */ cycle = FETCH; if (ir == IR_JMP) cpma = ([pdp8 getIB] << 12) | (cpma & 07777); else { cpma = ([pdp8 getIF] << 12) | (([pdp8 getPC] + 1) & 07777); [self setDatabus]; if (ir == IR_IOT) pause = YES; } } } } } - (void) eaeExecute /* check for memory cycles of EAE operate instructions */ { switch (md) { case 07403 : /* SCL */ if ([pdp8 getEAEmode] == EAE_MODE_A) { cpma = ([pdp8 getIF] << 12) | (([pdp8 getPC] + 1) & 07777); md = [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; } break; case 07405 : /* MUY */ case 07407 : /* DVI */ cpma = ([pdp8 getIF] << 12) | (([pdp8 getPC] + 1) & 07777); md = [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; if ([pdp8 getEAEmode] == EAE_MODE_B) { if ((cpma & 07770) == 010) { autoindex = cpma; md = (md + 1) & 07777; mdDir = MB_TO_MEM; } cpma = ([pdp8 getDF] << 12) | md; md = (cpma == autoindex) ? ([pdp8 memoryAt:cpma] + 1) & 07777 : [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; } break; case 07413 : /* SHL */ case 07415 : /* ASR */ case 07417 : /* LSR */ cpma = ([pdp8 getIF] << 12) | (([pdp8 getPC] + 1) & 07777); md = [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; break; case 07443 : /* DAD */ if ([pdp8 getEAEmode] == EAE_MODE_B) { cpma = ([pdp8 getIF] << 12) | (([pdp8 getPC] + 1) & 07777); md = [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; if ((cpma & 07770) == 010) { autoindex = cpma; md = (md + 1) & 07777; mdDir = MB_TO_MEM; } cpma = ([pdp8 getDF] << 12) | ((md + 1) & 07777); md = (cpma == autoindex) ? ([pdp8 memoryAt:cpma] + 1) & 07777 : [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; } break; case 07445 : /* DST */ if ([pdp8 getEAEmode] == EAE_MODE_B) { cpma = ([pdp8 getIF] << 12) | (([pdp8 getPC] + 1) & 07777); md = [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; if ((cpma & 07770) == 010) { autoindex = cpma; md = (md + 1) & 07777; mdDir = MB_TO_MEM; } cpma = ([pdp8 getDF] << 12) | ((md + 1) & 07777); md = [pdp8 getAC]; mdDir = MB_TO_MEM; } break; } } - (void) executeSingleCycle { switch (cycle) { case FETCH : autoindex = -1; bus = [pdp8 getAC]; if ([pdp8 getEnable] && ! ([pdp8 getDelay] || [pdp8 getInhibit]) && [pdp8 interruptRequest]) { interruptInProgress = YES; /* interrupt */ cpma = 0; ir = IR_JMS; cycle = EXECUTE; } else { /* normal instruction fetch */ cpma = [pdp8 getProgramCounter]; md = [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; ir = (md & 07000) >> 3; cpma = ([pdp8 getIF] << 12) | ((md & 0200) ? (cpma & 07600) : 0) | (md & 0177); if (ir <= IR_JMP && (md & 0400)) cycle = DEFER; else if (ir < IR_JMP) cycle = EXECUTE; else { [self setDatabus]; [self eaeExecute]; if (ir == IR_IOT) { bus = [pdp8 getAC]; pause = YES; } [pdp8 step]; } } break; case DEFER : if ((cpma & 07770) == 00010) { /* autoindex */ autoindex = cpma; md = ([pdp8 memoryAt:cpma] + 1) & 07777; mdDir = MB_TO_MEM; /* incremented value is written back */ } else { md = [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; } cpma = ([pdp8 getDF] << 12) | md; cycle = EXECUTE; break; case EXECUTE : switch (ir) { case IR_AND : md = (cpma == autoindex) ? ([pdp8 memoryAt:cpma] + 1) & 07777 : [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; break; case IR_TAD : md = (cpma == autoindex) ? ([pdp8 memoryAt:cpma] + 1) & 07777 : [pdp8 memoryAt:cpma]; mdDir = MB_FROM_MEM; break; case IR_ISZ : md = (cpma == autoindex) ? ([pdp8 memoryAt:cpma] + 1) & 07777 : [pdp8 memoryAt:cpma]; md = (md + 1) & 07777; mdDir = MB_TO_MEM; break; case IR_DCA : bus = md = [pdp8 getAC]; mdDir = MB_TO_MEM; break; case IR_JMS : md = ([pdp8 getPC] + (interruptInProgress ? 0 : 1)) & 07777; mdDir = MB_TO_MEM; break; case IR_JMP : break; } interruptInProgress = NO; cycle = FETCH; [pdp8 step]; break; } } - (void) examine { md = [pdp8 memoryAt:cpma]; [pdp8 setPC:([pdp8 getPC] + 1) & 07777]; cpma = [pdp8 getProgramCounter]; } - (void) deposit { cpma = [pdp8 getProgramCounter]; md = bus = [pdp8 getSR]; if ([pdp8 memorySize] > cpma) [pdp8 setMemoryAtAddress:cpma toValue:md]; mdDir = MB_TO_MEM; [pdp8 setPC:([pdp8 getPC] + 1) & 07777]; cpma = [pdp8 getProgramCounter]; } @end