1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-29 12:50:28 +00:00

Merge branch 'master' into AppleIIgs

This commit is contained in:
Thomas Harte 2020-12-29 22:16:23 -05:00
commit ee5f45c979
3 changed files with 326 additions and 30 deletions

View File

@ -180,29 +180,40 @@ void DiskII::set_state_machine(const std::vector<uint8_t> &state_machine) {
if(state_machine[0] != 0x18) { if(state_machine[0] != 0x18) {
for(size_t source_address = 0; source_address < 256; ++source_address) { for(size_t source_address = 0; source_address < 256; ++source_address) {
// Remap into Beneath Apple Pro-DOS address form. // Remap into Beneath Apple Pro-DOS address form.
size_t destination_address = const size_t destination_address =
((source_address&0x80) ? 0x10 : 0x00) |
((source_address&0x01) ? 0x20 : 0x00) |
((source_address&0x40) ? 0x40 : 0x00) |
((source_address&0x20) ? 0x80 : 0x00) | ((source_address&0x20) ? 0x80 : 0x00) |
((source_address&0x10) ? 0x01 : 0x00) | ((source_address&0x40) ? 0x40 : 0x00) |
((source_address&0x01) ? 0x20 : 0x00) |
((source_address&0x80) ? 0x10 : 0x00) |
((source_address&0x08) ? 0x08 : 0x00) | ((source_address&0x08) ? 0x08 : 0x00) |
((source_address&0x04) ? 0x04 : 0x00) | ((source_address&0x04) ? 0x04 : 0x00) |
((source_address&0x02) ? 0x02 : 0x00); ((source_address&0x02) ? 0x02 : 0x00) |
uint8_t source_value = state_machine[source_address]; ((source_address&0x10) ? 0x01 : 0x00);
source_value = // Store.
const uint8_t source_value = state_machine[source_address];
state_machine_[destination_address] =
((source_value & 0x80) ? 0x10 : 0x0) | ((source_value & 0x80) ? 0x10 : 0x0) |
((source_value & 0x40) ? 0x20 : 0x0) | ((source_value & 0x40) ? 0x20 : 0x0) |
((source_value & 0x20) ? 0x40 : 0x0) | ((source_value & 0x20) ? 0x40 : 0x0) |
((source_value & 0x10) ? 0x80 : 0x0) | ((source_value & 0x10) ? 0x80 : 0x0) |
(source_value & 0x0f); (source_value & 0x0f);
// Store.
state_machine_[destination_address] = source_value;
} }
} else { } else {
memcpy(&state_machine_[0], &state_machine[0], 128); for(size_t source_address = 0; source_address < 256; ++source_address) {
// Reshuffle ordering of bytes only, to retain indexing by the high nibble.
const size_t destination_address =
((source_address&0x80) ? 0x80 : 0x00) |
((source_address&0x40) ? 0x40 : 0x00) |
((source_address&0x01) ? 0x20 : 0x00) |
((source_address&0x20) ? 0x10 : 0x00) |
((source_address&0x08) ? 0x08 : 0x00) |
((source_address&0x04) ? 0x04 : 0x00) |
((source_address&0x02) ? 0x02 : 0x00) |
((source_address&0x10) ? 0x01 : 0x00);
state_machine_[destination_address] = state_machine[source_address];
}
} }
} }

View File

@ -10,17 +10,35 @@
@import IOKit; @import IOKit;
#include <IOKit/hid/IOHIDLib.h> #include <IOKit/hid/IOHIDLib.h>
@import GameController;
#pragma mark - CSJoystickButton #pragma mark - CSJoystickButton
@implementation CSJoystickButton { @implementation CSJoystickButton {
@package
bool _isPressed;
}
- (instancetype)initWithIndex:(NSInteger)index {
if (self = [super init]) {
_index = index;
}
return self;
}
@end
@interface CSIOJoystickButton: CSJoystickButton
@end
@implementation CSIOJoystickButton {
IOHIDElementRef _element; IOHIDElementRef _element;
} }
- (instancetype)initWithElement:(IOHIDElementRef)element index:(NSInteger)index { - (instancetype)initWithElement:(IOHIDElementRef)element index:(NSInteger)index {
self = [super init]; self = [super initWithIndex:index];
if(self) { if(self) {
_index = index;
_element = (IOHIDElementRef)CFRetain(element); _element = (IOHIDElementRef)CFRetain(element);
} }
return self; return self;
@ -31,7 +49,7 @@
} }
- (NSString *)description { - (NSString *)description {
return [NSString stringWithFormat:@"<CSJoystickButton: %p>; button %ld, %@", self, (long)self.index, self.isPressed ? @"pressed" : @"released"]; return [NSString stringWithFormat:@"<CSIOJoystickButton: %p>; button %ld, %@", self, (long)self.index, self.isPressed ? @"pressed" : @"released"];
} }
- (IOHIDElementRef)element { - (IOHIDElementRef)element {
@ -47,14 +65,33 @@
#pragma mark - CSJoystickAxis #pragma mark - CSJoystickAxis
@implementation CSJoystickAxis { @implementation CSJoystickAxis {
@package
float _position;
}
- (instancetype)initWithType:(CSJoystickAxisType)type
{
if (self = [super init]) {
_type = type;
}
return self;
}
@end
@interface CSIOJoystickAxis: CSJoystickAxis
@end
@implementation CSIOJoystickAxis {
IOHIDElementRef _element; IOHIDElementRef _element;
} }
- (instancetype)initWithElement:(IOHIDElementRef)element type:(CSJoystickAxisType)type { - (instancetype)initWithElement:(IOHIDElementRef)element type:(CSJoystickAxisType)type {
self = [super init]; self = [super initWithType:type];
if(self) { if(self) {
_element = (IOHIDElementRef)CFRetain(element); _element = (IOHIDElementRef)CFRetain(element);
_type = type;
_position = 0.5f; _position = 0.5f;
} }
return self; return self;
@ -65,7 +102,7 @@
} }
- (NSString *)description { - (NSString *)description {
return [NSString stringWithFormat:@"<CSJoystickAxis: %p>; type %d, value %0.2f", self, (int)self.type, self.position]; return [NSString stringWithFormat:@"<CSIOJoystickAxis: %p>; type %d, value %0.2f", self, (int)self.type, self.position];
} }
- (IOHIDElementRef)element { - (IOHIDElementRef)element {
@ -81,6 +118,18 @@
#pragma mark - CSJoystickHat #pragma mark - CSJoystickHat
@implementation CSJoystickHat { @implementation CSJoystickHat {
@package
CSJoystickHatDirection _direction;
}
@end
@interface CSIOJoystickHat: CSJoystickHat
@end
@implementation CSIOJoystickHat {
IOHIDElementRef _element; IOHIDElementRef _element;
} }
@ -97,7 +146,7 @@
} }
- (NSString *)description { - (NSString *)description {
return [NSString stringWithFormat:@"<CSJoystickHat: %p>; direction %ld", self, (long)self.direction]; return [NSString stringWithFormat:@"<CSIOJoystickHat: %p>; direction %ld", self, (long)self.direction];
} }
- (IOHIDElementRef)element { - (IOHIDElementRef)element {
@ -113,6 +162,24 @@
#pragma mark - CSJoystick #pragma mark - CSJoystick
@implementation CSJoystick { @implementation CSJoystick {
@package
NSArray<CSJoystickButton *> *_buttons;
NSArray<CSJoystickAxis *> *_axes;
NSArray<CSJoystickHat *> *_hats;
}
- (void)update
{
//subclass!
}
@end
@interface CSIOJoystick: CSJoystick
@end
@implementation CSIOJoystick {
IOHIDDeviceRef _device; IOHIDDeviceRef _device;
} }
@ -142,12 +209,12 @@
} }
- (NSString *)description { - (NSString *)description {
return [NSString stringWithFormat:@"<CSJoystick: %p>; buttons %@, axes %@, hats %@", self, self.buttons, self.axes, self.hats]; return [NSString stringWithFormat:@"<CSIOJoystick: %p>; buttons %@, axes %@, hats %@", self, self.buttons, self.axes, self.hats];
} }
- (void)update { - (void)update {
// Update buttons. // Update buttons.
for(CSJoystickButton *button in _buttons) { for(CSIOJoystickButton *button in _buttons) {
IOHIDValueRef value; IOHIDValueRef value;
if(IOHIDDeviceGetValue(_device, button.element, &value) == kIOReturnSuccess) { if(IOHIDDeviceGetValue(_device, button.element, &value) == kIOReturnSuccess) {
// Some pressure-sensitive buttons return values greater than 1 for hard presses, // Some pressure-sensitive buttons return values greater than 1 for hard presses,
@ -157,7 +224,7 @@
} }
// Update hats. // Update hats.
for(CSJoystickHat *hat in _hats) { for(CSIOJoystickHat *hat in _hats) {
IOHIDValueRef value; IOHIDValueRef value;
if(IOHIDDeviceGetValue(_device, hat.element, &value) == kIOReturnSuccess) { if(IOHIDDeviceGetValue(_device, hat.element, &value) == kIOReturnSuccess) {
// Hats report a direction, which is either one of eight or one of four. // Hats report a direction, which is either one of eight or one of four.
@ -181,7 +248,7 @@
} }
// Update axes. // Update axes.
for(CSJoystickAxis *axis in _axes) { for(CSIOJoystickAxis *axis in _axes) {
IOHIDValueRef value; IOHIDValueRef value;
if(IOHIDDeviceGetValue(_device, axis.element, &value) == kIOReturnSuccess) { if(IOHIDDeviceGetValue(_device, axis.element, &value) == kIOReturnSuccess) {
const CFIndex integerValue = IOHIDValueGetIntegerValue(value) - IOHIDElementGetLogicalMin(axis.element); const CFIndex integerValue = IOHIDValueGetIntegerValue(value) - IOHIDElementGetLogicalMin(axis.element);
@ -197,11 +264,157 @@
@end @end
#pragma mark - GameController subclasses
API_AVAILABLE(macos(11.0))
@interface CSGCJoystickHat: CSJoystickHat
@property (readonly, strong) GCDeviceDirectionPad *directionPad;
@end
API_AVAILABLE(macos(11.0))
@interface CSGCJoystickAxis: CSJoystickAxis
@property (readonly, strong) GCDeviceAxisInput *axis;
@end
API_AVAILABLE(macos(11.0))
@interface CSGCJoystickButton: CSJoystickButton
@property (readonly, strong) GCDeviceButtonInput *button;
@end
API_AVAILABLE(macos(11.0))
@interface CSGCJoystick: CSJoystick
@property (readonly, strong) GCController *device;
@end
@implementation CSGCJoystickHat
- (instancetype)initWithDirectionPad:(GCDeviceDirectionPad*)dPad {
if (self = [super init]) {
_directionPad = dPad;
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<CSGCJoystickHat: %p>; direction %ld", self, (long)self.direction];
}
- (void)setDirection:(CSJoystickHatDirection)direction {
_direction = direction;
}
@end
@implementation CSGCJoystickAxis
- (instancetype)initWithAxis:(GCDeviceAxisInput*)element type:(CSJoystickAxisType)type {
self = [super initWithType:type];
if(self) {
_axis = element;
_position = 0.5f;
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<CSGCJoystickAxis: %p>; type %d, value %0.2f", self, (int)self.type, self.position];
}
- (void)setPosition:(float)position {
_position = position;
}
@end
@implementation CSGCJoystickButton
- (instancetype)initWithButton:(GCDeviceButtonInput*)element index:(NSInteger)index {
self = [super initWithIndex:index];
if(self) {
_button = element;
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<CSGCJoystickButton: %p>; button %ld, %@", self, (long)self.index, self.isPressed ? @"pressed" : @"released"];
}
- (void)setIsPressed:(bool)isPressed {
_isPressed = isPressed;
}
@end
@implementation CSGCJoystick
- (instancetype)initWithButtons:(NSArray<CSJoystickButton *> *)buttons
axes:(NSArray<CSJoystickAxis *> *)axes
hats:(NSArray<CSJoystickHat *> *)hats
device:(GCController*)device {
if (self = [super init]) {
// Sort buttons by index.
_buttons = [buttons sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]];
// Sort axes by enum value.
_axes = [axes sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"type" ascending:YES]]];
// Hats have no guaranteed ordering.
_hats = hats;
// Keep hold of the device.
_device = device;
}
return self;
}
-(void)update {
// Update buttons.
for(CSGCJoystickButton *button in _buttons) {
// This assumes that the values provided by GCDeviceButtonInput are
// digital. This might not always be the case.
button.isPressed = button.button.pressed;
}
for(CSGCJoystickAxis *axis in _axes) {
float val = axis.axis.value;
val += 1;
val /= 2;
axis.position = val;
}
for(CSGCJoystickHat *hat in _hats) {
// This assumes that the values provided by GCDeviceDirectionPad are
// digital. this might not always be the case.
CSJoystickHatDirection hatDir = 0;
if (hat.directionPad.down.pressed) {
hatDir |= CSJoystickHatDirectionDown;
}
if (hat.directionPad.up.pressed) {
hatDir |= CSJoystickHatDirectionUp;
}
if (hat.directionPad.left.pressed) {
hatDir |= CSJoystickHatDirectionLeft;
}
if (hat.directionPad.right.pressed) {
hatDir |= CSJoystickHatDirectionRight;
}
// There shouldn't be any conflicting directions.
hat.direction = hatDir;
}
}
- (NSString *)description {
return [NSString stringWithFormat:@"<CSGCJoystick: %p>; buttons %@, axes %@, hats %@", self, self.buttons, self.axes, self.hats];
}
@end
#pragma mark - CSJoystickManager #pragma mark - CSJoystickManager
@interface CSJoystickManager () @interface CSJoystickManager ()
- (void)deviceMatched:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender; - (void)deviceMatched:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender;
- (void)deviceRemoved:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender; - (void)deviceRemoved:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender;
- (void)controllerDidConnect:(NSNotification *)note API_AVAILABLE(macos(11.0));
- (void)controllerDidDisconnect:(NSNotification *)note API_AVAILABLE(macos(11.0));
@end @end
static void DeviceMatched(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) { static void DeviceMatched(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) {
@ -235,6 +448,10 @@ static void DeviceRemoved(void *context, IOReturn result, void *sender, IOHIDDev
IOHIDManagerRegisterDeviceMatchingCallback(_hidManager, DeviceMatched, (__bridge void *)self); IOHIDManagerRegisterDeviceMatchingCallback(_hidManager, DeviceMatched, (__bridge void *)self);
IOHIDManagerRegisterDeviceRemovalCallback(_hidManager, DeviceRemoved, (__bridge void *)self); IOHIDManagerRegisterDeviceRemovalCallback(_hidManager, DeviceRemoved, (__bridge void *)self);
IOHIDManagerScheduleWithRunLoop(_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); IOHIDManagerScheduleWithRunLoop(_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
if (@available(macOS 11.0, *)) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerDidConnect:) name:GCControllerDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerDidDisconnect:) name:GCControllerDidDisconnectNotification object:nil];
}
if(IOHIDManagerOpen(_hidManager, kIOHIDOptionsTypeNone) != kIOReturnSuccess) { if(IOHIDManagerOpen(_hidManager, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
NSLog(@"Failed to open HID manager"); NSLog(@"Failed to open HID manager");
@ -246,15 +463,80 @@ static void DeviceRemoved(void *context, IOReturn result, void *sender, IOHIDDev
return self; return self;
} }
- (void)controllerDidConnect:(NSNotification *)note {
GCController *controller = note.object;
// Double check this joystick isn't already known.
for(CSGCJoystick *joystick in _joysticks) {
if (![joystick isKindOfClass:[CSGCJoystick class]]) {
continue;
}
if([joystick.device isEqual:controller]) return;
}
// Prepare to collate a list of buttons, axes and hats for the new device.
NSMutableArray<CSJoystickButton *> *buttons = [[NSMutableArray alloc] init];
NSMutableArray<CSJoystickAxis *> *axes = [[NSMutableArray alloc] init];
NSMutableArray<CSJoystickHat *> *hats = [[NSMutableArray alloc] init];
if (controller.extendedGamepad) {
GCExtendedGamepad *gp = controller.extendedGamepad;
// Let's go a b x y
// 1 2 3 4
[buttons addObject:[[CSGCJoystickButton alloc] initWithButton:gp.buttonA index:1]];
[buttons addObject:[[CSGCJoystickButton alloc] initWithButton:gp.buttonB index:2]];
[buttons addObject:[[CSGCJoystickButton alloc] initWithButton:gp.buttonX index:3]];
[buttons addObject:[[CSGCJoystickButton alloc] initWithButton:gp.buttonY index:4]];
[hats addObject:[[CSGCJoystickHat alloc] initWithDirectionPad:gp.dpad]];
[axes addObject:[[CSGCJoystickAxis alloc] initWithAxis:gp.leftThumbstick.xAxis type:CSJoystickAxisTypeX]];
[axes addObject:[[CSGCJoystickAxis alloc] initWithAxis:gp.leftThumbstick.yAxis type:CSJoystickAxisTypeY]];
[axes addObject:[[CSGCJoystickAxis alloc] initWithAxis:gp.rightThumbstick.xAxis type:CSJoystickAxisTypeZ]];
} else {
return;
}
// Add this joystick to the list.
[_joysticks addObject:[[CSGCJoystick alloc] initWithButtons:buttons axes:axes hats:hats device:controller]];
}
- (void)controllerDidDisconnect:(NSNotification *)note {
GCController *controller = note.object;
// If this joystick was recorded, remove it.
for(CSGCJoystick *joystick in [_joysticks copy]) {
if (![joystick isKindOfClass:[CSGCJoystick class]]) {
continue;
}
if([joystick.device isEqual:controller]) {
[_joysticks removeObject:joystick];
return;
}
}
}
- (void)dealloc { - (void)dealloc {
IOHIDManagerUnscheduleFromRunLoop(_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); IOHIDManagerUnscheduleFromRunLoop(_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
IOHIDManagerClose(_hidManager, kIOHIDOptionsTypeNone); IOHIDManagerClose(_hidManager, kIOHIDOptionsTypeNone);
CFRelease(_hidManager); CFRelease(_hidManager);
if (@available(macOS 11.0, *)) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:GCControllerDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:GCControllerDidDisconnectNotification object:nil];
}
} }
- (void)deviceMatched:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender { - (void)deviceMatched:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender {
if (@available(macOS 11.0, *)) {
if ([GCController supportsHIDDevice:device]) {
return;
}
}
// Double check this joystick isn't already known. // Double check this joystick isn't already known.
for(CSJoystick *joystick in _joysticks) { for(CSIOJoystick *joystick in _joysticks) {
if (![joystick isKindOfClass:[CSIOJoystick class]]) {
continue;
}
if(joystick.device == device) return; if(joystick.device == device) return;
} }
@ -279,7 +561,7 @@ static void DeviceRemoved(void *context, IOReturn result, void *sender, IOHIDDev
case kIOHIDElementTypeInput_Button: { case kIOHIDElementTypeInput_Button: {
// Add a button; pretty easy stuff. 'Usage' provides a button index. // Add a button; pretty easy stuff. 'Usage' provides a button index.
const uint32_t usage = IOHIDElementGetUsage(element); const uint32_t usage = IOHIDElementGetUsage(element);
[buttons addObject:[[CSJoystickButton alloc] initWithElement:element index:usage]]; [buttons addObject:[[CSIOJoystickButton alloc] initWithElement:element index:usage]];
} break; } break;
case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Misc:
@ -296,25 +578,28 @@ static void DeviceRemoved(void *context, IOReturn result, void *sender, IOHIDDev
// A hatswitch is a multi-directional control all of its own. // A hatswitch is a multi-directional control all of its own.
case kHIDUsage_GD_Hatswitch: case kHIDUsage_GD_Hatswitch:
[hats addObject:[[CSJoystickHat alloc] initWithElement:element]]; [hats addObject:[[CSIOJoystickHat alloc] initWithElement:element]];
continue; continue;
} }
// Add the axis; if it was a hat switch or unrecognised then the code doesn't // Add the axis; if it was a hat switch or unrecognised then the code doesn't
// reach here. // reach here.
[axes addObject:[[CSJoystickAxis alloc] initWithElement:element type:axisType]]; [axes addObject:[[CSIOJoystickAxis alloc] initWithElement:element type:axisType]];
} break; } break;
} }
} }
CFRelease(elements); CFRelease(elements);
// Add this joystick to the list. // Add this joystick to the list.
[_joysticks addObject:[[CSJoystick alloc] initWithButtons:buttons axes:axes hats:hats device:device]]; [_joysticks addObject:[[CSIOJoystick alloc] initWithButtons:buttons axes:axes hats:hats device:device]];
} }
- (void)deviceRemoved:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender { - (void)deviceRemoved:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender {
// If this joystick was recorded, remove it. // If this joystick was recorded, remove it.
for(CSJoystick *joystick in [_joysticks copy]) { for(CSIOJoystick *joystick in [_joysticks copy]) {
if (![joystick isKindOfClass:[CSIOJoystick class]]) {
continue;
}
if(joystick.device == device) { if(joystick.device == device) {
[_joysticks removeObject:joystick]; [_joysticks removeObject:joystick];
return; return;

View File

@ -83,4 +83,4 @@ This emulator attempts cycle-accurate emulation of all supported machines. In so
I've been asked several times whether it is possible to sponsor this project; I think that's a poor fit for this emulator's highly-malleable scope, and it makes me uncomfortable because as the author I primarily see only its defects. I've been asked several times whether it is possible to sponsor this project; I think that's a poor fit for this emulator's highly-malleable scope, and it makes me uncomfortable because as the author I primarily see only its defects.
An Amazon US wishlist is now attached in the hope of avoiding the question in future. A lot of it is old books now available only secondhand — I like to read about potential future additions well in advance of starting on them. Per the optimism of some book sellers, please don't purchase anything that is currnetly listed only at an absurd price. An Amazon US wishlist is now attached in the hope of avoiding the question in future. A lot of it is old books now available only secondhand — I like to read about potential future additions well in advance of starting on them. Per the optimism of some book sellers, please don't purchase anything that is currnetly listed only at an absurd price; they were sorted by secondhand price when added to the list, with cheapest being $5.