mirror of
https://github.com/ksherlock/ample.git
synced 2024-11-15 22:07:28 +00:00
540 lines
14 KiB
Objective-C
540 lines
14 KiB
Objective-C
//
|
|
// SlotViewController.m
|
|
// Ample
|
|
//
|
|
// Created by Kelvin Sherlock on 8/18/2020.
|
|
// Copyright © 2020 Kelvin Sherlock. All rights reserved.
|
|
//
|
|
|
|
#import "SlotViewController.h"
|
|
|
|
const unsigned kMemoryMask = 1 << 16;
|
|
|
|
@interface SlotViewController () {
|
|
|
|
unsigned _slots_explicit;
|
|
unsigned _slots_valid;
|
|
unsigned _slots_default;
|
|
|
|
NSDictionary *_slot_object[14];
|
|
NSDictionary *_slot_media[14];
|
|
NSDictionary *_machine_media;
|
|
}
|
|
|
|
@property (weak) IBOutlet NSPopUpButton *ram_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *sl0_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *sl1_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *sl2_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *sl3_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *sl4_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *sl5_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *sl6_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *sl7_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *exp_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *aux_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *rs232_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *game_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *printer_menu;
|
|
@property (weak) IBOutlet NSPopUpButton *modem_menu;
|
|
|
|
|
|
@end
|
|
|
|
@implementation SlotViewController
|
|
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
// Do view setup here.
|
|
|
|
//[self setModel: @"apple2gs"];
|
|
}
|
|
|
|
-(void)reset {
|
|
|
|
}
|
|
|
|
|
|
-(void)setModel:(NSString *)model {
|
|
|
|
if (model == _model) return;
|
|
if ([model isEqualToString: _model]) return;
|
|
|
|
_model = model;
|
|
|
|
[self loadMachine: model];
|
|
}
|
|
|
|
-(void)resetMachine {
|
|
|
|
[self setMachine: @{}];
|
|
#if 0
|
|
[self setSl0: @""];
|
|
[self setSl1: @""];
|
|
[self setSl2: @""];
|
|
[self setSl3: @""];
|
|
[self setSl4: @""];
|
|
[self setSl5: @""];
|
|
[self setSl6: @""];
|
|
[self setSl7: @""];
|
|
|
|
[self setRs232: @""];
|
|
[self setAux: @""];
|
|
[self setExp: @""];
|
|
[self setGameio: @""];
|
|
[self setPrinter: @""];
|
|
[self setModem: @""];
|
|
|
|
[self setMemory: @""];
|
|
[self setMemoryBytes: 0];
|
|
#endif
|
|
[self setResolution: NSMakeSize(0, 0)];
|
|
|
|
_slots_default = 0;
|
|
//_slots_explicit = 0;
|
|
_slots_valid = 0;
|
|
|
|
_machine_media = nil;
|
|
|
|
[self setArgs: @[]];
|
|
[self setMedia: @{}];
|
|
|
|
|
|
#if 0
|
|
// retain for later?
|
|
for (unsigned i = 0; i < 14; ++i) {
|
|
_slot_media[i] = nil;
|
|
_slot_object[i] = nil;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static NSFont *ItalicMenuFont(void) {
|
|
NSFont *font = [NSFont menuFontOfSize: 0];
|
|
NSFontDescriptor *fd = [font fontDescriptor];
|
|
NSFontDescriptor *fd2 = [fd fontDescriptorWithSymbolicTraits: NSFontDescriptorTraitItalic];
|
|
return [NSFont fontWithDescriptor: fd2 size: [font pointSize]];
|
|
}
|
|
|
|
// entry 0 is None/Empty for slots, but populated for RAM.
|
|
static int SetDefaultMenu(NSArray *items, NSPopUpButton *button) {
|
|
|
|
static NSDictionary *attr = nil;
|
|
if (!attr) {
|
|
attr = @{
|
|
NSFontAttributeName: ItalicMenuFont()
|
|
};
|
|
}
|
|
|
|
unsigned ix = 0;
|
|
|
|
|
|
for (NSDictionary *d in items) {
|
|
BOOL def = [(NSNumber *)[d objectForKey: @"default"] boolValue];
|
|
if (!def) {
|
|
++ix;
|
|
continue;
|
|
}
|
|
|
|
NSMenuItem *item = [button itemAtIndex: ix];
|
|
NSString *title = [d objectForKey: @"description"];
|
|
NSAttributedString *t = [[NSAttributedString alloc] initWithString: title attributes: attr];
|
|
[item setAttributedTitle: t];
|
|
return ix;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void DeactivateMenus(NSArray *items, NSPopUpButton *button) {
|
|
|
|
[button setAutoenablesItems: NO];
|
|
unsigned ix = 0;
|
|
for (NSDictionary *d in items) {
|
|
BOOL value = [(NSNumber *)[d objectForKey: @"disabled"] boolValue];
|
|
if (value) {
|
|
|
|
NSMenuItem *item = [button itemAtIndex: ix];
|
|
[item setEnabled: NO];
|
|
}
|
|
++ix;
|
|
}
|
|
|
|
}
|
|
|
|
-(void)syncMemory {
|
|
|
|
int ix = 0;
|
|
NSArray *items = [_machine objectForKey: @"ram"];
|
|
|
|
unsigned default_index = SetDefaultMenu(items, _ram_menu);
|
|
_slots_valid |= kMemoryMask;
|
|
_slots_default &= ~kMemoryMask;
|
|
if (default_index) _slots_default |= kMemoryMask;
|
|
|
|
if (_slots_explicit & kMemoryMask) {
|
|
// if ram was explicitly set, try to keep it.
|
|
|
|
for (NSDictionary *d in items) {
|
|
unsigned size = [(NSNumber *)[d objectForKey: @"intValue"] unsignedIntValue];
|
|
if (size == _memoryBytes) {
|
|
[_ram_menu selectItemAtIndex: ix];
|
|
[self setMemory: [d objectForKey: @"value"]];
|
|
return;
|
|
}
|
|
++ix;
|
|
}
|
|
}
|
|
|
|
_slots_explicit &= ~kMemoryMask;
|
|
if (default_index) {
|
|
NSDictionary *d = [items objectAtIndex: default_index];
|
|
|
|
[_ram_menu selectItemAtIndex: default_index];
|
|
[self setMemory: [d objectForKey: @"value"]];
|
|
[self setMemoryBytes: [(NSNumber *)[d objectForKey: @"intValue"] unsignedIntValue]];
|
|
} else {
|
|
[self setMemoryBytes: 0];
|
|
[self setMemory: @""];
|
|
[_ram_menu selectItemAtIndex: 0];
|
|
}
|
|
}
|
|
|
|
-(void)resetMemory {
|
|
|
|
NSArray *items = [_machine objectForKey: @"ram"];
|
|
if (![items count]) return;
|
|
|
|
unsigned ix = 0;
|
|
for (NSDictionary *d in items) {
|
|
BOOL def = [(NSNumber *)[d objectForKey: @"default"] boolValue];
|
|
if (!def) {
|
|
++ix;
|
|
continue;
|
|
}
|
|
// ram should always have a default.
|
|
[_ram_menu selectItemAtIndex: ix];
|
|
[self setMemory: [d objectForKey: @"description"]];
|
|
_slots_default |= kMemoryMask;
|
|
return;
|
|
}
|
|
// just in case
|
|
[_ram_menu selectItemAtIndex: 0];
|
|
}
|
|
|
|
-(void)syncSlot: (NSString *)slot button: (NSPopUpButton *)button index: (unsigned)index {
|
|
|
|
NSString *value = [self valueForKey: slot];
|
|
NSArray *items = [_machine objectForKey: slot];
|
|
|
|
unsigned mask = 1 << index;
|
|
|
|
_slots_default &= ~mask;
|
|
_slots_valid &= ~mask;
|
|
|
|
if (![items count]) {
|
|
//[self setValue: @"" forKey: slot]; // retain for later.
|
|
//_slots_explicit &= ~mask;
|
|
|
|
return;
|
|
}
|
|
_slots_valid |= mask;
|
|
|
|
DeactivateMenus(items, button);
|
|
unsigned default_index = SetDefaultMenu(items, button);
|
|
|
|
if (default_index) _slots_default |= mask;
|
|
|
|
|
|
if (_slots_explicit & mask) {
|
|
int ix = 0;
|
|
for (NSDictionary *d in items) {
|
|
if ([value isEqualToString: [d objectForKey: @"value"]]) {
|
|
|
|
[button selectItemAtIndex: ix];
|
|
_slot_object[index] = d;
|
|
_slot_media[index] = [d objectForKey: @"media"];
|
|
return;
|
|
}
|
|
++ix;
|
|
}
|
|
}
|
|
_slots_explicit &= ~mask;
|
|
|
|
if (default_index) {
|
|
NSDictionary *d = [items objectAtIndex: default_index];
|
|
[button selectItemAtIndex: default_index];
|
|
[self setValue: [d objectForKey: @"value"] forKey: slot];
|
|
_slot_object[index] = d;
|
|
_slot_media[index] = [d objectForKey: @"media"];
|
|
} else {
|
|
[button selectItemAtIndex: 0];
|
|
[self setValue: @"" forKey: slot];
|
|
_slot_object[index] = nil;
|
|
_slot_media[index] = nil;
|
|
}
|
|
}
|
|
|
|
-(void)resetSlot: (NSString *)slot button: (NSPopUpButton *)button index: (unsigned)index {
|
|
|
|
NSArray *items = [_machine objectForKey: slot];
|
|
if (![items count]) return;
|
|
|
|
unsigned ix = 0;
|
|
for (NSDictionary *d in items) {
|
|
BOOL def = [(NSNumber *)[d objectForKey: @"default"] boolValue];
|
|
if (!def) {
|
|
++ix;
|
|
continue;
|
|
}
|
|
[button selectItemAtIndex: ix];
|
|
[self setValue: [d objectForKey: @"value"] forKey: slot];
|
|
_slot_object[index] = d;
|
|
_slot_media[index] = [d objectForKey: @"media"];
|
|
|
|
_slots_default |= (1 << index);
|
|
return;
|
|
}
|
|
// just in case
|
|
NSDictionary *d = [items firstObject];
|
|
|
|
[button selectItemAtIndex: 0];
|
|
[self setValue: [d objectForKey: @"value"] forKey: slot];
|
|
_slot_object[index] = d;
|
|
_slot_media[index] = [d objectForKey: @"media"];
|
|
}
|
|
|
|
-(void)syncSlots {
|
|
|
|
[self syncMemory];
|
|
[self syncSlot: @"sl0" button: _sl0_menu index: 0];
|
|
[self syncSlot: @"sl1" button: _sl1_menu index: 1];
|
|
[self syncSlot: @"sl2" button: _sl2_menu index: 2];
|
|
[self syncSlot: @"sl3" button: _sl3_menu index: 3];
|
|
[self syncSlot: @"sl4" button: _sl4_menu index: 4];
|
|
[self syncSlot: @"sl5" button: _sl5_menu index: 5];
|
|
[self syncSlot: @"sl6" button: _sl6_menu index: 6];
|
|
[self syncSlot: @"sl7" button: _sl7_menu index: 7];
|
|
[self syncSlot: @"exp" button: _exp_menu index: 8];
|
|
[self syncSlot: @"aux" button: _aux_menu index: 9];
|
|
[self syncSlot: @"rs232" button: _rs232_menu index: 10];
|
|
[self syncSlot: @"gameio" button: _game_menu index: 11];
|
|
[self syncSlot: @"printer" button: _printer_menu index: 12];
|
|
[self syncSlot: @"modem" button: _modem_menu index: 13];
|
|
}
|
|
|
|
-(void)loadMachine: (NSString *)model {
|
|
|
|
if (!model) {
|
|
[self resetMachine];
|
|
return;
|
|
}
|
|
|
|
NSBundle *bundle = [NSBundle mainBundle];
|
|
NSURL *url= [bundle URLForResource: model withExtension: @"plist"];
|
|
|
|
NSDictionary *d = [NSDictionary dictionaryWithContentsOfURL: url];
|
|
|
|
if (!d) {
|
|
[self resetMachine];
|
|
return;
|
|
}
|
|
|
|
NSArray *r = [d objectForKey: @"resolution"];
|
|
NSSize res = NSMakeSize(0, 0);
|
|
if (r) {
|
|
res.width = [(NSNumber *)[r objectAtIndex: 0 /*@"width"*/] doubleValue];
|
|
res.height = [(NSNumber *)[r objectAtIndex: 1 /*@"height"*/] doubleValue];
|
|
}
|
|
[self setResolution: res];
|
|
|
|
_machine_media = [d objectForKey: @"media"];
|
|
|
|
// n.b. - does content binding propogate immediately?
|
|
[self setMachine: d];
|
|
[self syncSlots];
|
|
[self rebuildArgs];
|
|
[self rebuildMedia];
|
|
}
|
|
|
|
static NSString *SlotNames[] = {
|
|
|
|
@"sl0", @"sl1", @"sl2", @"sl3",
|
|
@"sl4", @"sl5", @"sl6", @"sl7",
|
|
@"exp", @"aux", @"rs232",
|
|
@"gameio", @"printer", @"modem",
|
|
};
|
|
|
|
unsigned SlotCount = 14;
|
|
|
|
- (IBAction)menuChanged:(NSPopUpButton *)sender {
|
|
|
|
|
|
|
|
NSInteger tag = [sender tag];
|
|
|
|
// NSInteger ix = [sender indexOfSelectedItem];
|
|
|
|
NSString *key = SlotNames[tag];
|
|
|
|
_slots_explicit |= (1 << tag);
|
|
|
|
NSDictionary *o = [[sender selectedItem] representedObject];
|
|
|
|
[self setValue: [o objectForKey: @"value"] forKey: key];
|
|
|
|
_slot_object[tag] = o;
|
|
NSDictionary *newMedia = [o objectForKey: @"media"];
|
|
NSDictionary *oldMedia = _slot_media[tag];
|
|
|
|
if (newMedia != oldMedia) {
|
|
_slot_media[tag] = newMedia;
|
|
[self rebuildMedia];
|
|
}
|
|
|
|
|
|
[self rebuildArgs];
|
|
}
|
|
|
|
- (IBAction)memoryMenuChanged:(NSPopUpButton *)sender {
|
|
|
|
//
|
|
NSDictionary *o = [[sender selectedItem] representedObject];
|
|
NSString *title = [o objectForKey: @"description"];
|
|
[self setMemory: title];
|
|
[self setMemoryBytes: [(NSNumber *)[o objectForKey: @"value"] unsignedIntValue]];
|
|
|
|
_slots_explicit |= kMemoryMask;
|
|
|
|
// if pull-down menu
|
|
if ([sender pullsDown])
|
|
[sender setTitle: title];
|
|
|
|
[self rebuildArgs];
|
|
}
|
|
|
|
-(IBAction)resetSlots:(id)sender {
|
|
/* reset slots to default */
|
|
|
|
_slots_explicit = 0;
|
|
_slots_default = 0;
|
|
for (unsigned i = 0 ; i < SlotCount; ++i) {
|
|
[self setValue: @"" forKey: SlotNames[i]];
|
|
_slot_media[i] = nil;
|
|
_slot_object[i] = nil;
|
|
}
|
|
_memory = @"";
|
|
_memoryBytes = 0;
|
|
|
|
[self resetMemory];
|
|
[self resetSlot: @"sl0" button: _sl0_menu index: 0];
|
|
[self resetSlot: @"sl1" button: _sl1_menu index: 1];
|
|
[self resetSlot: @"sl2" button: _sl2_menu index: 2];
|
|
[self resetSlot: @"sl3" button: _sl3_menu index: 3];
|
|
[self resetSlot: @"sl4" button: _sl4_menu index: 4];
|
|
[self resetSlot: @"sl5" button: _sl5_menu index: 5];
|
|
[self resetSlot: @"sl6" button: _sl6_menu index: 6];
|
|
[self resetSlot: @"sl7" button: _sl7_menu index: 7];
|
|
[self resetSlot: @"exp" button: _exp_menu index: 8];
|
|
[self resetSlot: @"aux" button: _aux_menu index: 9];
|
|
[self resetSlot: @"rs232" button: _rs232_menu index: 10];
|
|
[self resetSlot: @"gameio" button: _game_menu index: 11];
|
|
[self resetSlot: @"printer" button: _printer_menu index: 12];
|
|
[self resetSlot: @"modem" button: _modem_menu index: 13];
|
|
|
|
[self rebuildArgs];
|
|
[self rebuildMedia];
|
|
}
|
|
|
|
static BOOL should_add_arg(unsigned slot, unsigned valid_slots, unsigned explicit_slots, unsigned default_slots, NSString *value) {
|
|
|
|
unsigned mask = 1 << slot;
|
|
if (~valid_slots & mask) return NO;
|
|
|
|
if (default_slots & mask) {
|
|
if (explicit_slots & mask)
|
|
return YES;
|
|
return NO;
|
|
}
|
|
return [value length];
|
|
}
|
|
|
|
-(void)rebuildArgs {
|
|
|
|
NSMutableArray *args = [NSMutableArray new];
|
|
|
|
|
|
/* if there IS a default card for the slot and nothing is selected, need to -sl0 "" it. */
|
|
|
|
|
|
#define _(ix, a, b) \
|
|
if (should_add_arg(ix, _slots_valid, _slots_explicit, _slots_default, a)) { \
|
|
[args addObject: b]; [args addObject: a]; \
|
|
} \
|
|
|
|
_(16, _memory, @"-ramsize")
|
|
|
|
_(0, _sl0, @"-sl0")
|
|
_(1, _sl1, @"-sl1")
|
|
_(2, _sl2, @"-sl2")
|
|
_(3, _sl3, @"-sl3")
|
|
_(4, _sl4, @"-sl4")
|
|
_(5, _sl5, @"-sl5")
|
|
_(6, _sl6, @"-sl6")
|
|
_(7, _sl7, @"-sl7")
|
|
_(8, _exp, @"-exp")
|
|
_(9, _aux, @"-aux")
|
|
_(10, _rs232, @"-rs232")
|
|
_(11, _gameio, @"-gameio")
|
|
_(12, _printer, @"-printer")
|
|
_(13, _modem, @"-modem")
|
|
#undef _
|
|
[self setArgs: args];
|
|
}
|
|
|
|
|
|
-(void)rebuildMedia {
|
|
|
|
|
|
#define _(var, o) var += [[o objectForKey: @ # var ] unsignedIntValue]
|
|
|
|
unsigned cass = 0;
|
|
unsigned cdrm = 0;
|
|
unsigned hard = 0;
|
|
unsigned flop_5_25 = 0;
|
|
unsigned flop_3_5 = 0;
|
|
|
|
unsigned mask = 1;
|
|
for (unsigned i = 0; i < 14; ++i, mask <<= 1) {
|
|
|
|
if (_slots_valid & mask) {
|
|
NSDictionary *tmp = _slot_media[i];
|
|
if (tmp) {
|
|
_(cass, tmp);
|
|
_(cdrm, tmp);
|
|
_(hard, tmp);
|
|
_(flop_5_25, tmp);
|
|
_(flop_3_5, tmp);
|
|
}
|
|
}
|
|
}
|
|
NSDictionary *tmp = _machine_media;
|
|
if (tmp) {
|
|
_(cass, tmp);
|
|
_(cdrm, tmp);
|
|
_(hard, tmp);
|
|
_(flop_5_25, tmp);
|
|
_(flop_3_5, tmp);
|
|
}
|
|
|
|
[self setMedia: @{
|
|
@"cass": @(cass),
|
|
@"cdrm": @(cdrm),
|
|
@"hard": @(hard),
|
|
@"flop_5_25": @(flop_5_25),
|
|
@"flop_3_5": @(flop_3_5),
|
|
}];
|
|
}
|
|
|
|
|
|
@end
|