428 lines
12 KiB
Objective-C
428 lines
12 KiB
Objective-C
/*
|
|
* PDP-8/E Simulator
|
|
*
|
|
* Copyright © 1994-2015 Bernhard Baehr
|
|
*
|
|
* RK8E.m - RK8-E Disk Cartridge System for the PDP-8/E Simulator
|
|
*
|
|
* 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 "PluginFramework/PluginAPI.h"
|
|
#import "PluginFramework/PDP8.h"
|
|
#import "PluginFramework/Utilities.h"
|
|
#import "PluginFramework/FileDropControlTargetProtocol.h"
|
|
#import "PluginFramework/NSThread+MainThread.h"
|
|
|
|
#define USE_RK8E_REGISTERS_DIRECTLY 1
|
|
|
|
#import "RK8E.h"
|
|
#import "RK8Eiot.h"
|
|
#import "RK05.h"
|
|
#import "RK05Controller.h"
|
|
|
|
|
|
#define CODER_KEY_COMMAND @"cmd"
|
|
#define CODER_KEY_STATUS @"status"
|
|
#define CODER_KEY_BLOCK @"block"
|
|
#define CODER_KEY_CURADDR @"curaddr"
|
|
#define CODER_KEY_IOFLAG @"ioflag"
|
|
#define CODER_KEY_IOMASK @"iomask"
|
|
#define CODER_KEY_MAINT @"maint"
|
|
#define CODER_KEY_LDB0 @"ldb0"
|
|
#define CODER_KEY_LDB1 @"ldb1"
|
|
#define CODER_KEY_LDB2 @"ldb2"
|
|
#define CODER_KEY_LDB3 @"ldb3"
|
|
#define CODER_KEY_LDBFILL @"ldbfill"
|
|
#define CODER_KEY_UDB @"udb"
|
|
#define CODER_KEY_CRC @"crc"
|
|
#define CODER_KEY_CHECKCRC @"checkcrc"
|
|
#define CODER_KEY_SHIFTS @"shifts"
|
|
#define CODER_KEY_WORDCOUNT @"wordcount"
|
|
#define CODER_KEY_SHIFT_ENABLE @"shiftena"
|
|
|
|
|
|
@implementation RK8E
|
|
|
|
|
|
API_VERSION
|
|
|
|
|
|
#pragma mark Plugin Methods
|
|
|
|
|
|
- (NSArray *) iotsForAddress:(int)ioAddress
|
|
{
|
|
return [NSArray arrayWithObjects:
|
|
[NSValue valueWithPointer:nil],
|
|
[NSValue valueWithPointer:i6741],
|
|
[NSValue valueWithPointer:i6742],
|
|
[NSValue valueWithPointer:i6743],
|
|
[NSValue valueWithPointer:i6744],
|
|
[NSValue valueWithPointer:i6745],
|
|
[NSValue valueWithPointer:i6746],
|
|
[NSValue valueWithPointer:i6747],
|
|
nil];
|
|
}
|
|
|
|
|
|
- (NSArray *) skiptestsForAddress:(int)ioAddress
|
|
{
|
|
return [NSArray arrayWithObjects:
|
|
[NSValue valueWithPointer:nil],
|
|
[NSValue valueWithPointer:s6741],
|
|
[NSValue valueWithPointer:nil],
|
|
[NSValue valueWithPointer:nil],
|
|
[NSValue valueWithPointer:nil],
|
|
[NSValue valueWithPointer:nil],
|
|
[NSValue valueWithPointer:nil],
|
|
[NSValue valueWithPointer:nil],
|
|
nil];
|
|
|
|
}
|
|
|
|
|
|
- (void) setIOFlag:(unsigned long)flag forIOFlagName:(NSString *)name;
|
|
{
|
|
ioflag = flag;
|
|
}
|
|
|
|
|
|
- (void) resetInternalRegisters
|
|
{
|
|
maint = NO;
|
|
ldb[0] = ldb[1] = ldb[2] = ldb[3] = 0;
|
|
ldbfill = 0;
|
|
udb = 0;
|
|
crc = 0;
|
|
checkcrc = 0;
|
|
shifts = 0;
|
|
wordcount = 0;
|
|
shiftEnabled = NO;
|
|
}
|
|
|
|
|
|
- (void) CAF:(int)ioAddress
|
|
{
|
|
command = 0;
|
|
block = 0;
|
|
status = [rk05[0] isMounted] ? 0 : STATUS_HEAD_IN_MOTION | STATUS_FILE_NOT_READY;
|
|
curaddr = 0;
|
|
[self resetInternalRegisters];
|
|
[pdp8 clearIOFlagBits:ioflag];
|
|
[pdp8 clearInterruptMaskBits:ioflag];
|
|
[rk05[0] abortAllCommands];
|
|
[rk05[1] abortAllCommands];
|
|
[rk05[2] abortAllCommands];
|
|
[rk05[3] abortAllCommands];
|
|
}
|
|
|
|
|
|
- (void) clearAllFlags:(int)ioAddress
|
|
{
|
|
[self setCommand:0];
|
|
[self setBlockNumber:0];
|
|
[self setStatus:0];
|
|
[self updateStatusMountFlags];
|
|
[self setMemoryAddress:0];
|
|
[self resetInternalRegisters];
|
|
[pdp8 clearIOFlagBits:ioflag];
|
|
[pdp8 clearInterruptMaskBits:ioflag];
|
|
[rk05[0] abortAllCommands];
|
|
[rk05[1] abortAllCommands];
|
|
[rk05[2] abortAllCommands];
|
|
[rk05[3] abortAllCommands];
|
|
}
|
|
|
|
|
|
- (void) resetDevice
|
|
{
|
|
[self clearAllFlags:0];
|
|
[rk05[0] setWriteProtected:NO];
|
|
[rk05[1] setWriteProtected:NO];
|
|
[rk05[2] setWriteProtected:NO];
|
|
[rk05[3] setWriteProtected:NO];
|
|
}
|
|
|
|
|
|
#pragma mark Register Access
|
|
|
|
|
|
- (unsigned short) getCommand
|
|
{
|
|
return command;
|
|
}
|
|
|
|
|
|
- (void) setCommand:(unsigned short)cmd
|
|
{
|
|
NSAssert1 ((cmd & ~07777) == 0, @"Bad RK8-E Command: 0%o", cmd);
|
|
command = cmd;
|
|
if (command & COMMAND_INTERRUPT_ON_DONE)
|
|
[pdp8 setInterruptMaskBits:ioflag];
|
|
else
|
|
[pdp8 clearInterruptMaskBits:ioflag];
|
|
[self updateStatusMountFlags];
|
|
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
|
|
[defaultCenter postNotificationName:COMMAND_CHANGED_NOTIFICATION object:self];
|
|
[defaultCenter postNotificationName:MEMORYADDRESS_CHANGED_NOTIFICATION object:self];
|
|
[defaultCenter postNotificationName:BLOCKNUMBER_CHANGED_NOTIFICATION object:self];
|
|
}
|
|
|
|
|
|
- (void) notifyPDP8IOFlagsChanged:(NSNotification *)notification
|
|
{
|
|
//NSLog (@"RK8E notifyPDP8IOFlagsChanged");
|
|
if ([pdp8 getInterruptMaskBits:ioflag])
|
|
command |= COMMAND_INTERRUPT_ON_DONE;
|
|
else
|
|
command &= ~COMMAND_INTERRUPT_ON_DONE;
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:COMMAND_CHANGED_NOTIFICATION object:self];
|
|
if ([pdp8 getIOFlagBits:ioflag]) {
|
|
if (! (status & STATUS_RAISE_FLAG_MASK))
|
|
status |= STATUS_DONE;
|
|
} else
|
|
status &= ~STATUS_RAISE_FLAG_MASK;
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:STATUS_CHANGED_NOTIFICATION object:self];
|
|
}
|
|
|
|
|
|
- (unsigned short) getStatus
|
|
{
|
|
return status;
|
|
}
|
|
|
|
|
|
- (void) setStatus:(unsigned short)stat
|
|
{
|
|
NSAssert1 ((stat & ~07777) == 0, @"Bad RK8-E Status: 0%o", stat);
|
|
status = stat;
|
|
if (status & STATUS_RAISE_FLAG_MASK)
|
|
[pdp8 setIOFlagBits:ioflag];
|
|
else
|
|
[pdp8 clearIOFlagBits:ioflag];
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:STATUS_CHANGED_NOTIFICATION object:self];
|
|
}
|
|
|
|
|
|
- (void) updateStatusMountFlags
|
|
{
|
|
NSAssertRunningOnMainThread ();
|
|
if ([rk05[(command >> 1) & 3] isMounted])
|
|
status &= ~(STATUS_HEAD_IN_MOTION | STATUS_FILE_NOT_READY);
|
|
else
|
|
status |= STATUS_HEAD_IN_MOTION | STATUS_FILE_NOT_READY;
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:STATUS_CHANGED_NOTIFICATION object:self];
|
|
}
|
|
|
|
|
|
- (void) notifyStatusChangedWhenNotRunning
|
|
{
|
|
NSAssertRunningOnMainThread ();
|
|
NSAssert (! [pdp8 isGoing], @"PDP-8 is running");
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:STATUS_CHANGED_NOTIFICATION object:self];
|
|
}
|
|
|
|
|
|
- (void) setStatusBits:(unsigned)set clearStatusBits:(unsigned)clear
|
|
{
|
|
status |= set;
|
|
status &= ~clear;
|
|
if (status & STATUS_RAISE_FLAG_MASK)
|
|
[pdp8 setIOFlagBits:ioflag];
|
|
else
|
|
[pdp8 clearIOFlagBits:ioflag];
|
|
if (! [pdp8 isGoing])
|
|
[self performSelectorOnMainThread:@selector(notifyStatusChangedWhenNotRunning)
|
|
withObject:nil waitUntilDone:YES];
|
|
}
|
|
|
|
|
|
- (unsigned short) getBlockNumber
|
|
{
|
|
return ((command & 1) << 12) | block;
|
|
}
|
|
|
|
|
|
- (void) setBlockNumber:(unsigned short)blk
|
|
{
|
|
NSAssert1 ((blk & ~017777) == 0, @"Bad RK8-E Block Number: 0%o", blk);
|
|
block = blk & 07777;
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:BLOCKNUMBER_CHANGED_NOTIFICATION
|
|
object:self];
|
|
[self setCommand:([self getCommand] & 07776) | (blk >> 12)];
|
|
}
|
|
|
|
|
|
- (unsigned short) getMemoryAddress
|
|
{
|
|
return ((command & 070) << 9) | curaddr;
|
|
}
|
|
|
|
|
|
- (void) setMemoryAddress:(unsigned short)addr
|
|
{
|
|
NSAssert1 ((addr & ~077777) == 0, @"Bad RK8-E Memory Address: 0%o", addr);
|
|
curaddr = addr & 07777;
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MEMORYADDRESS_CHANGED_NOTIFICATION
|
|
object:self];
|
|
[self setCommand:([self getCommand] & 07707) | ((addr >> 9) & 070)];
|
|
}
|
|
|
|
|
|
- (unsigned short) getCurrentAddress
|
|
{
|
|
return curaddr;
|
|
}
|
|
|
|
|
|
- (void) setCurrentAddress:(unsigned short)addr
|
|
{
|
|
NSAssert1 ((addr & ~07777) == 0, @"Bad Current Address: 0%o", addr);
|
|
curaddr = addr;
|
|
}
|
|
|
|
|
|
- (unsigned short) getCurrentDriveNumber
|
|
{
|
|
return (command >> 1) & 3;
|
|
}
|
|
|
|
|
|
- (void) setCRC:(unsigned)newCRC
|
|
{
|
|
crc = checkcrc = newCRC;
|
|
}
|
|
|
|
|
|
- (BOOL) isCRCOK
|
|
{
|
|
return crc == checkcrc;
|
|
}
|
|
|
|
|
|
- (void) lockControl
|
|
{
|
|
[controlLock lock];
|
|
}
|
|
|
|
|
|
- (void) unlockControl
|
|
{
|
|
[controlLock unlock];
|
|
}
|
|
|
|
|
|
#pragma mark Initialization
|
|
|
|
|
|
- (id) initWithCoder:(NSCoder *)coder
|
|
{
|
|
self = [super init];
|
|
[self setCommand:[coder decodeIntForKey:CODER_KEY_COMMAND]];
|
|
[self setStatus:[coder decodeIntForKey:CODER_KEY_STATUS]];
|
|
[self setBlockNumber:[coder decodeIntForKey:CODER_KEY_BLOCK]];
|
|
[self setCurrentAddress:[coder decodeIntForKey:CODER_KEY_CURADDR]];
|
|
[coder decodeBoolForKey:CODER_KEY_IOFLAG] ?
|
|
[pdp8 setIOFlagBits:ioflag] : [pdp8 clearIOFlagBits:ioflag];
|
|
[coder decodeBoolForKey:CODER_KEY_IOMASK] ?
|
|
[pdp8 setInterruptMaskBits:ioflag] : [pdp8 clearInterruptMaskBits:ioflag];
|
|
maint = [coder decodeBoolForKey:CODER_KEY_MAINT];
|
|
ldb[0] = [coder decodeIntForKey:CODER_KEY_LDB0];
|
|
ldb[1] = [coder decodeIntForKey:CODER_KEY_LDB1];
|
|
ldb[2] = [coder decodeIntForKey:CODER_KEY_LDB2];
|
|
ldb[3] = [coder decodeIntForKey:CODER_KEY_LDB3];
|
|
ldbfill = [coder decodeIntForKey:CODER_KEY_LDBFILL];
|
|
udb = [coder decodeIntForKey:CODER_KEY_UDB];
|
|
crc = [coder decodeIntForKey:CODER_KEY_CRC];
|
|
checkcrc = [coder decodeIntForKey:CODER_KEY_CHECKCRC];
|
|
shifts = [coder decodeIntForKey:CODER_KEY_SHIFTS];
|
|
wordcount = [coder decodeIntForKey:CODER_KEY_WORDCOUNT];
|
|
shiftEnabled = [coder decodeBoolForKey:CODER_KEY_SHIFT_ENABLE];
|
|
return self;
|
|
}
|
|
|
|
|
|
- (void) encodeWithCoder:(NSCoder *)coder
|
|
{
|
|
[coder encodeInt:[self getCommand] forKey:CODER_KEY_COMMAND];
|
|
[coder encodeInt:[self getStatus] forKey:CODER_KEY_STATUS];
|
|
[coder encodeInt:[self getBlockNumber] forKey:CODER_KEY_BLOCK];
|
|
[coder encodeInt:[self getCurrentAddress] forKey:CODER_KEY_CURADDR];
|
|
[coder encodeBool:[pdp8 getIOFlagBits:ioflag] ? YES : NO forKey:CODER_KEY_IOFLAG];
|
|
[coder encodeBool:[pdp8 getInterruptMaskBits:ioflag] ? YES : NO forKey:CODER_KEY_IOMASK];
|
|
[coder encodeBool:maint forKey:CODER_KEY_MAINT];
|
|
[coder encodeInt:ldb[0] forKey:CODER_KEY_LDB0];
|
|
[coder encodeInt:ldb[1] forKey:CODER_KEY_LDB1];
|
|
[coder encodeInt:ldb[2] forKey:CODER_KEY_LDB2];
|
|
[coder encodeInt:ldb[3] forKey:CODER_KEY_LDB3];
|
|
[coder encodeInt:ldbfill forKey:CODER_KEY_LDBFILL];
|
|
[coder encodeInt:udb forKey:CODER_KEY_UDB];
|
|
[coder encodeInt:crc forKey:CODER_KEY_CRC];
|
|
[coder encodeInt:checkcrc forKey:CODER_KEY_CHECKCRC];
|
|
[coder encodeInt:shifts forKey:CODER_KEY_SHIFTS];
|
|
[coder encodeInt:wordcount forKey:CODER_KEY_WORDCOUNT];
|
|
[coder encodeBool:shiftEnabled forKey:CODER_KEY_SHIFT_ENABLE];
|
|
}
|
|
|
|
|
|
- (void) notifyApplicationWillTerminate:(NSNotification *)notification
|
|
{
|
|
// NSLog (@"RK8E notifyApplicationWillTerminate");
|
|
NSMutableData *data = [NSMutableData data];
|
|
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
|
|
[self encodeWithCoder:archiver];
|
|
[rk05Controller_0 encodeWithCoder:archiver];
|
|
[rk05Controller_1 encodeWithCoder:archiver];
|
|
[rk05Controller_2 encodeWithCoder:archiver];
|
|
[rk05Controller_3 encodeWithCoder:archiver];
|
|
[archiver finishEncoding];
|
|
[archiver release];
|
|
[[NSUserDefaults standardUserDefaults] setObject:data forKey:[self pluginName]];
|
|
}
|
|
|
|
|
|
- (void) pluginDidLoad
|
|
{
|
|
rk05[0] = rk05_0; [rk05[0] setPDP8:pdp8];
|
|
rk05[1] = rk05_1; [rk05[1] setPDP8:pdp8];
|
|
rk05[2] = rk05_2; [rk05[2] setPDP8:pdp8];
|
|
rk05[3] = rk05_3; [rk05[3] setPDP8:pdp8];
|
|
NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:[self pluginName]];
|
|
if (data) {
|
|
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
|
|
self = [self initWithCoder:unarchiver];
|
|
[rk05Controller_0 initWithCoder:unarchiver];
|
|
[rk05Controller_1 initWithCoder:unarchiver];
|
|
[rk05Controller_2 initWithCoder:unarchiver];
|
|
[rk05Controller_3 initWithCoder:unarchiver];
|
|
[unarchiver finishDecoding];
|
|
[unarchiver release];
|
|
}
|
|
controlLock = [[NSLock alloc] init];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(notifyApplicationWillTerminate:)
|
|
name:NSApplicationWillTerminateNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyPDP8IOFlagsChanged:)
|
|
name:IOFLAGS_CHANGED_NOTIFICATION object:nil];
|
|
}
|
|
|
|
|
|
@end
|