/*
 *	PDP-8/E Simulator
 *
 *	Copyright © 1994-2015 Bernhard Baehr
 *
 *	SkipController.h - Controller for the skip indicator in the CPU window
 *
 *	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 "SkipController.h"
#import "TableCornerView.h"
#import "PDP8.h"
#import "mri.h"
#import "eae.h"
#import "iot.h"
#import "opr.h"


#define UPDATE_SKIPINDICATOR_NOTIFICATION	@"updateSkipIndicatorNotification"


#define SKIP_IMAGE_NAME				@"skipArrow"
#define INTERRUPT_IMAGE_NAME			@"interruptArrow"


// Key Value pair for the skiptest dictionary initialization
#define KV(i, s)	[NSValue valueWithPointer:s], [NSValue valueWithPointer:i]


typedef unsigned (*PDP8SkiptestFunctionPointer)(void);
				

@implementation SkipController


- (void) awakeFromNib
{
	NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];

	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:MEMORY_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:PROGRAM_COUNTER_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:ACCUMULATOR_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:GTF_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:MQ_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:EAE_MODE_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:DF_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:UF_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:ENABLE_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:DELAY_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:INHIBIT_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:IOFLAGS_CHANGED_NOTIFICATION object:nil];
	[defaultCenter addObserver:self selector:@selector(notifySkipIndicator:)
		name:SF_CHANGED_NOTIFICATION object:nil];	// required for TSC8 ESME
	[defaultCenter addObserver:self selector:@selector(notifyUpdateSkipIndicator:)
		name:UPDATE_SKIPINDICATOR_NOTIFICATION object:nil]; 
	/* PDP-8 notifications that probably can't change the skip/interrupt indicator:
	   SR_CHANGED_NOTIFICATION
	   SC_CHANGED_NOTIFICATION
	   UB_CHANGED_NOTIFICATION
	   SF_CHANGED_NOTIFICATION
	*/
	dictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
	/* MRI skip instructions */
		KV(i2000, s2000), KV(i2200, s2200), KV(i2400, s2400), KV(i2410, s2410),
		KV(i2600, s2600), KV(i2610, s2610),
	/* EAE skip instructions */
		KV(i7451, s7451),
	/* IOT skip instructions */
		KV(i6000, s6000), KV(i6003, s6003), KV(i6006, s6006), KV(i6254, s6254),
	/* OPR skip instructions */
		KV(i7410, s7410), KV(i7412, s7410), KV(i7414, s7410), KV(i7416, s7410),
		KV(i7420, s7420), KV(i7422, s7420), KV(i7424, s7420), KV(i7426, s7420),
		KV(i7430, s7430), KV(i7432, s7430), KV(i7434, s7430), KV(i7436, s7430),
		KV(i7440, s7440), KV(i7442, s7440), KV(i7444, s7440), KV(i7446, s7440),
		KV(i7450, s7450), KV(i7452, s7450), KV(i7454, s7450), KV(i7456, s7450),
		KV(i7460, s7460), KV(i7462, s7460), KV(i7464, s7460), KV(i7466, s7460),
		KV(i7470, s7470), KV(i7472, s7470), KV(i7474, s7470), KV(i7476, s7470),
		KV(i7500, s7500), KV(i7502, s7500), KV(i7504, s7500), KV(i7506, s7500),
		KV(i7510, s7510), KV(i7512, s7510), KV(i7514, s7510), KV(i7516, s7510),
		KV(i7520, s7520), KV(i7522, s7520), KV(i7524, s7520), KV(i7526, s7520),
		KV(i7530, s7530), KV(i7532, s7530), KV(i7534, s7530), KV(i7536, s7530),
		KV(i7540, s7540), KV(i7542, s7540), KV(i7544, s7540), KV(i7546, s7540),
		KV(i7550, s7550), KV(i7552, s7550), KV(i7554, s7550), KV(i7556, s7550),
		KV(i7610, s7410), KV(i7612, s7410), KV(i7614, s7410), KV(i7616, s7410),
		KV(i7620, s7420), KV(i7622, s7420), KV(i7624, s7420), KV(i7626, s7420),
		KV(i7630, s7430), KV(i7632, s7430), KV(i7634, s7430), KV(i7636, s7430),
		KV(i7640, s7440), KV(i7642, s7440), KV(i7644, s7440), KV(i7646, s7440),
		KV(i7650, s7450), KV(i7652, s7450), KV(i7654, s7450), KV(i7656, s7450),
		KV(i7660, s7460), KV(i7662, s7460), KV(i7664, s7460), KV(i7666, s7460),
		KV(i7670, s7470), KV(i7672, s7470), KV(i7674, s7470), KV(i7676, s7470),
		KV(i7700, s7500), KV(i7702, s7500), KV(i7704, s7500), KV(i7706, s7500),
		KV(i7710, s7510), KV(i7712, s7510), KV(i7714, s7510), KV(i7716, s7510),
		KV(i7720, s7520), KV(i7722, s7520), KV(i7724, s7520), KV(i7726, s7520),
		KV(i7730, s7530), KV(i7732, s7530), KV(i7734, s7530), KV(i7736, s7530),
		KV(i7740, s7540), KV(i7742, s7540), KV(i7744, s7540), KV(i7746, s7540),
		KV(i7750, s7550), KV(i7752, s7550), KV(i7754, s7550), KV(i7756, s7550),
		KV(i7760, s7560), KV(i7762, s7560), KV(i7764, s7560), KV(i7766, s7560),
		KV(i7770, s7570), KV(i7772, s7570), KV(i7774, s7570), KV(i7776, s7570),
		nil
	];
}


- (void) notifySkipIndicator:(NSNotification *)notification
{
	/* coalesc the multiple ..._CHANGED_NOTIFICATIONS to one UPDATE_SKIPINDICATOR_NOTIFICATION
	   to avoid time consuming repeated updates */
	// NSLog (@"notifySkipIndicator");
	[[NSNotificationQueue defaultQueue] enqueueNotification:
		[NSNotification notificationWithName:UPDATE_SKIPINDICATOR_NOTIFICATION object:self]
		postingStyle:NSPostASAP coalesceMask:NSNotificationCoalescingOnName forModes:nil];
}


- (void) notifyUpdateSkipIndicator:(NSNotification *)notification
{
	// NSLog (@"SkipController notifyUpdateSkipIndicator");
	NSAssert (! [pdp8 isGoing], @"PDP-8 is running");
	if ([pdp8 getEnable] && ([pdp8 getIOFlagBits:~0] & [pdp8 getInterruptMaskBits:~0]) &&
		! ([pdp8 getDelay] || [pdp8 getInhibit]))
		[view setImageNamed:INTERRUPT_IMAGE_NAME toolTip:
			NSLocalizedString(@"An interrupt is pending", @"")];
	else {
		NSValue *skipfunc = [dictionary objectForKey:
			[NSValue valueWithPointer:[pdp8 getNextInstruction]]];
		if (skipfunc) {
			if (((PDP8SkiptestFunctionPointer)[skipfunc pointerValue])())
				[view setImageNamed:SKIP_IMAGE_NAME toolTip:
					NSLocalizedString(@"The next instruction will be skipped", @"")];
			else
				[view setImageNamed:nil toolTip:nil];
		} else
			[view setImageNamed:nil toolTip:nil];
	}
}


- (void) addSkiptest:(NSValue *)skiptest forInstruction:(NSValue *)instruction
{
	if (skiptest && [skiptest pointerValue] && instruction && [instruction pointerValue])
		[dictionary setObject:skiptest forKey:instruction];
}


@end
