Files
Bliss-Core/BlissGameCore.mm
clobber cd54554708 Build fix for latest Clang.
BlissGameCore.mm:208:9: error: cannot initialize a variable of type 'CHAR *' (aka 'char *') with an rvalue of type 'const char *'
BlissGameCore.mm:220:9: error: cannot initialize a variable of type 'CHAR *' (aka 'char *') with an rvalue of type 'const char *'
BlissGameCore.mm:232:9: error: cannot initialize a variable of type 'CHAR *' (aka 'char *') with an rvalue of type 'const char *'
2018-04-05 02:40:46 -05:00

975 lines
25 KiB
Plaintext

/*
Copyright (c) 2014, OpenEmu Team
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the OpenEmu Team nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "BlissGameCore.h"
#import <IOKit/hid/IOHIDLib.h>
#import <OpenEmuBase/OERingBuffer.h>
#import <OpenGL/gl.h>
#import "OEIntellivisionSystemResponderClient.h"
#import "core/Emulator.h"
#import "core/rip/Rip.h"
#import "core/audio/AudioMixer.h"
#import "core/video/VideoBus.h"
#import "drivers/intv/Intellivision.h"
#import "drivers/intv/HandController.h"
#import "drivers/intv/ECSKeyboard.h"
#define INTV_IMAGE_WIDTH (160)
#define INTV_IMAGE_HEIGHT (192)
#define KEYBOARD_OBJECT_COUNT 256
#define AUDIO_SAMPLE_RATE 48000
#define INTY_TO_BITMAP(bits) (1ULL << (uint64_t)(bits))
#define INTY_TEST(bits, flag) ((bits) & (uint64_t)(flag))
#define INTY_ON(bits, flag) ((bits) |= (uint64_t)(flag))
#define INTY_OFF(bits, flag) ((bits) &= ~(uint64_t)(flag))
typedef struct
{
UINT16 keypad;
UINT16 action;
UINT16 disc;
} BlissController;
class BlissInputProducer : public InputProducer
{
public:
BlissInputProducer();
const CHAR* getName() { return "Bliss Input"; }
void poll() {}
INT32 getInputCount() { return 23; }
const CHAR* getInputName(INT32) { return "Bliss Input Name"; }
float getValue(INT32 enumeration);
BOOL isKeyboardDevice() { return keyboardDevice; }
void setKeyboardDevice(BOOL isKeyboardDevice) {
keyboardDevice = isKeyboardDevice;
}
void setPlayer(CHAR playerIndex) {
player = playerIndex;
}
private:
CHAR player;
BOOL keyboardDevice;
};
class BlissAudioMixer : public AudioMixer
{
public:
void init(UINT32 sampleRate);
void release();
void flushAudio();
};
class BlissVideoBus : public VideoBus
{
public:
void init(UINT32 width, UINT32 height);
void release();
void render();
};
@interface BlissGameCore () <OEIntellivisionSystemResponderClient>
{
NSLock *_bufferLock;
OERingBuffer *_audioBuffer;
unsigned char *_videoBuffer;
BlissAudioMixer *_audioMixer;
BlissVideoBus *_videoBus;
NSString *_ROMName;
Emulator *currentEmu;
Rip *currentRip;
NSMutableData *_stateData;
}
- (int)blissButtonForIntellivisionButton:(OEIntellivisionButton)button player:(NSUInteger)player;
@end
@implementation BlissGameCore
// Global variables because the callbacks need to access them...
static BlissGameCore *_currentCore;
static BlissController _controller[2] = {0};
static uint64_t _keyboard = 0;
static uint8_t _keyboardDownCount = 0;
static uint8_t _keyboardShiftCount = 0;
#pragma mark - OpenEmu Core
/*
OpenEmu Core internal functions
*/
- (id)init
{
self = [super init];
if(self != nil)
{
_bufferLock = [[NSLock alloc] init];
_currentCore = self;
_audioMixer = new BlissAudioMixer;
_videoBus = new BlissVideoBus;
_stateData = [NSMutableData dataWithLength:sizeof(IntellivisionState)];
}
return self;
}
- (void)dealloc
{
DLog(@"releasing/deallocating Bliss memory");
delete _videoBus;
_videoBus = NULL;
delete _audioMixer;
_audioMixer = NULL;
_currentCore = NULL;
}
- (void)executeFrame
{
//DLog(@"Executing");
// run the emulation
currentEmu->Run();
// render and display the video
currentEmu->Render();
// flush the audio
currentEmu->FlushAudio();
}
#pragma mark - Bliss Core Helpers
- (BOOL)LoadRip:(const char*)filename
{
char cfgFilename[PATH_MAX] = {0};
NSString *cfgString = [[NSBundle bundleForClass:[self class]] pathForResource:@"knowncarts" ofType:@"cfg" inDirectory:@""];
strncpy(cfgFilename, cfgString.fileSystemRepresentation, sizeof(cfgFilename));
if(!cfgFilename[0])
return FALSE;
if(strlen(filename) < 5)
return FALSE;
const CHAR* extStart = filename + strlen(filename) - 4;
if(strcmpi(extStart, ".intv") == 0 || strcmpi(extStart, ".int") == 0 || strcmpi(extStart, ".bin") == 0)
{
//load the bin file as a Rip
currentRip = Rip::LoadBin(filename, cfgFilename);
if(currentRip == NULL)
return FALSE;
}
else if(strcmpi(extStart, ".a52") == 0)
{
//load the bin file as a Rip
currentRip = Rip::LoadA52(filename);
if(currentRip == NULL)
return FALSE;
CHAR fileSubname[MAX_PATH];
const CHAR* filenameStart = strrchr(filename, '/')+1;
strncpy(fileSubname, filenameStart, strlen(filenameStart)-4);
*(fileSubname+strlen(filenameStart)-4) = NULL;
}
else if(strcmpi(extStart, ".irom") == 0 || strcmpi(extStart, ".rom") == 0)
{
//load the rom file as a Rip
currentRip = Rip::LoadRom(filename);
if(currentRip == NULL)
return FALSE;
CHAR fileSubname[MAX_PATH];
const CHAR* filenameStart = strrchr(filename, '/')+1;
strncpy(fileSubname, filenameStart, strlen(filenameStart)-4);
*(fileSubname+strlen(filenameStart)-4) = NULL;
}
else if(strcmpi(extStart, ".zip") == 0)
{
//load the zip file as a Rip
currentRip = Rip::LoadZip(filename, cfgFilename);
if(currentRip == NULL)
return FALSE;
CHAR fileSubname[MAX_PATH];
const CHAR* filenameStart = strrchr(filename, '/')+1;
strncpy(fileSubname, filenameStart, strlen(filenameStart)-4);
*(fileSubname+strlen(filenameStart)-4) = NULL;
}
else
{
//load the designated Rip
currentRip = Rip::LoadRip(filename);
if(currentRip == NULL)
return FALSE;
}
return TRUE;
}
- (BOOL)loadROMForPeripheral:(Peripheral*)peripheral
{
BOOL didLoadROMs = NO;
NSString *BIOSPath = nil;
UINT16 count = peripheral->GetROMCount();
for(UINT16 i = 0; i < count; i++)
{
ROM* r = peripheral->GetROM(i);
if(r->isLoaded())
{
didLoadROMs = YES;
continue;
}
BIOSPath = [[self biosDirectoryPath] stringByAppendingString:[NSString stringWithFormat:@"/%s", r->getDefaultFileName()]];
if(r->load([BIOSPath fileSystemRepresentation], r->getDefaultFileOffset()))
{
didLoadROMs = YES;
}
else
{
didLoadROMs = NO;
break;
}
}
return didLoadROMs;
}
- (void)ReleasePeripheralInputs:(Peripheral*)periph
{
UINT16 count = periph->GetInputConsumerCount();
for(UINT16 i = 0; i < count; i++)
{
InputConsumer* nextInputConsumer = periph->GetInputConsumer(i);
//iterate through each object on this consumer (buttons, keys, etc.)
int iccount = nextInputConsumer->getInputConsumerObjectCount();
for(int j = 0; j < iccount; j++)
{
InputConsumerObject* nextObject = nextInputConsumer->getInputConsumerObject(j);
if(nextObject)
{
nextObject->clearBindings();
}
}
}
}
- (void)ReleaseEmulatorInputs
{
memset(_controller, 0, sizeof(_controller));
_keyboard = 0;
_keyboardDownCount = 0;
_keyboardShiftCount = 0;
if(!currentEmu)
return;
[self ReleasePeripheralInputs:currentEmu];
UINT32 count = currentEmu->GetPeripheralCount();
for(UINT32 i = 0; i < count; i++)
{
[self ReleasePeripheralInputs:currentEmu->GetPeripheral(i)];
}
}
- (void)InitializePeripheralInputs:(Peripheral*)periph
{
//iterate through all the emulated input consumers in the current emulator.
//these consumers represent the emulated joysticks, keyboards, etc. that were
//originally used to provide input to the emulated system
UINT16 count = periph->GetInputConsumerCount();
for(UINT16 i = 0; i < count; i++)
{
InputConsumer* nextInputConsumer = periph->GetInputConsumer(i);
BOOL isKeyboard = dynamic_cast<ECSKeyboard*>(nextInputConsumer) ? TRUE : FALSE;
//iterate through each object on this consumer (buttons, keys, etc.)
int iccount = nextInputConsumer->getInputConsumerObjectCount();
for(int j = 0; j < iccount; j++)
{
InputConsumerObject* nextObject = nextInputConsumer->getInputConsumerObject(j);
if(nextObject)
{
INT32 _objectids[1] = {nextObject->getDefaultEnum()};
INT32 *objectids = _objectids;
InputProducer** producerList = new InputProducer*[0];
BlissInputProducer *producer = new BlissInputProducer;
producer->setPlayer(i);
producer->setKeyboardDevice(isKeyboard);
producerList[0] = producer;
nextObject->addBinding(producerList, objectids, 1);
delete[] producerList;
}
}
}
}
- (void)InitializeEmulatorInputs
{
[self ReleaseEmulatorInputs];
[self InitializePeripheralInputs:currentEmu];
UINT32 count = currentEmu->GetPeripheralCount();
for(UINT32 i = 0; i < count; i++)
{
[self InitializePeripheralInputs:currentEmu->GetPeripheral(i)];
}
}
#pragma mark - OpenEmu Core
- (BOOL)loadFileAtPath:(NSString *)path error:(NSError **)error
{
_ROMName = [path copy];
if(![self LoadRip:path.fileSystemRepresentation])
{
return FALSE;
}
DLog(@"Loaded File");
// find the currentEmulator required to run this RIP
currentEmu = Emulator::GetEmulatorByID(currentRip->GetTargetSystemID());
// load emulator ROMs
if(![self loadROMForPeripheral:currentEmu])
{
return NO;
}
// load peripheral ROMs
INT32 count = currentEmu->GetPeripheralCount();
for(INT32 i = 0; i < count; i++)
{
Peripheral* p = currentEmu->GetPeripheral(i);
PeripheralCompatibility usage = currentRip->GetPeripheralUsage(p->GetShortName());
if(usage == PERIPH_INCOMPATIBLE || usage == PERIPH_COMPATIBLE)
{
currentEmu->UsePeripheral(i, FALSE);
continue;
}
BOOL loaded = [self loadROMForPeripheral:p];
if(loaded)
{
//peripheral loaded, might as well use it.
currentEmu->UsePeripheral(i, TRUE);
}
else if(usage == PERIPH_OPTIONAL)
{
//didn't load, but the peripheral is optional, so just skip it
currentEmu->UsePeripheral(i, FALSE);
}
else
{
//usage == PERIPH_REQUIRED, but it didn't load
return NO;
}
}
[self InitializeEmulatorInputs];
// hook up the audio and video
currentEmu->InitVideo(_videoBus, currentEmu->GetVideoWidth(), currentEmu->GetVideoHeight());
currentEmu->InitAudio(_audioMixer, AUDIO_SAMPLE_RATE);
// put the RIP in the currentEmulator
currentEmu->SetRip(currentRip);
// finally, run everything
currentEmu->Reset();
return YES;
}
- (void)resetEmulation
{
currentEmu->Reset();
}
- (void)stopEmulation
{
if(currentEmu)
{
currentEmu->SetRip(NULL);
currentEmu->ReleaseAudio();
currentEmu->ReleaseVideo();
currentEmu = NULL;
}
if(currentRip)
{
delete currentRip;
currentRip = NULL;
}
[super stopEmulation];
}
- (OEIntSize)bufferSize
{
return OEIntSizeMake(INTV_IMAGE_WIDTH, INTV_IMAGE_HEIGHT);
}
- (OEIntRect)screenRect
{
return OEIntRectMake(0, 0, INTV_IMAGE_WIDTH, INTV_IMAGE_HEIGHT);
}
- (OEIntSize)aspectSize
{
return OEIntSizeMake(INTV_IMAGE_WIDTH * (12.0/7.0), INTV_IMAGE_HEIGHT);
}
- (const void *)getVideoBufferWithHint:(void *)hint
{
if (!hint) {
if (!_videoBuffer) _videoBuffer = new unsigned char[256 * 256 * 4];
hint = _videoBuffer;
}
return _videoBuffer = (uint8_t*)hint;
}
- (GLenum)pixelFormat
{
return GL_BGRA;
}
- (GLenum)pixelType
{
return GL_UNSIGNED_INT_8_8_8_8_REV;
}
- (NSTimeInterval)frameInterval
{
// http://spatula-city.org/~im14u2c/intv/tech/master.html
// Actual Effective Frame Rate
return 59.92;
}
- (NSUInteger)channelCount
{
return 1;
}
- (double)audioSampleRate
{
return AUDIO_SAMPLE_RATE;
}
- (NSUInteger)audioBitDepth
{
return 16;
}
- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
{
BOOL didSaveStateFile = NO;
didSaveStateFile = currentEmu->SaveStateFile([fileName fileSystemRepresentation]);
block(didSaveStateFile, nil);
}
- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
{
BOOL didLoadStateFile = NO;
// TODO: is this an emulator bug, a state bug, or a hardware necessity?
// determine if this intellivision cart requires the ECS peripheral.
// if it does, we need to 'warm up' the system with 5 frames before loading
// the state in to the emulator. (only immediately following a reset - why?)
if(currentRip->GetPeripheralUsage("ECS") == PERIPH_REQUIRED)
{
int warmUpECSFrameCount = 5;
while(warmUpECSFrameCount > 0)
{
currentEmu->Run();
warmUpECSFrameCount--;
}
}
didLoadStateFile = currentEmu->LoadStateFile([fileName fileSystemRepresentation]);
block(didLoadStateFile, nil);
}
- (NSData *)serializeStateWithError:(NSError **)outError
{
void *stateBuffer = [_stateData mutableBytes];
NSUInteger stateLength = [_stateData length];
BOOL didSaveStateData = NO;
didSaveStateData = currentEmu->SaveStateBuffer(stateBuffer, stateLength);
if(didSaveStateData)
return _stateData;
if(outError) {
*outError = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotSaveStateError userInfo:@{
NSLocalizedDescriptionKey : @"Save state data could not be written",
NSLocalizedRecoverySuggestionErrorKey : @"The emulator could not write the state data."
}];
}
return nil;
}
- (BOOL)deserializeState:(NSData *)state withError:(NSError **)outError
{
const void *stateBuffer = [state bytes];
NSUInteger stateLength = [_stateData length];
BOOL didLoadStateData = NO;
didLoadStateData = currentEmu->LoadStateBuffer(stateBuffer, stateLength);
if(didLoadStateData)
return YES;
if(outError) {
*outError = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotLoadStateError userInfo:@{
NSLocalizedDescriptionKey : @"The save state data could not be read"
}];
}
return NO;
}
#pragma mark -
/*
Bliss callbacks
*/
#pragma mark Bliss Audio Mixer
void BlissAudioMixer::init(UINT32 sampleRate)
{
int sampleInterval = (sampleRate / [_currentCore frameInterval]);
// initialize the sampleBuffer
AudioMixer::init(sampleRate);
_currentCore->_audioBuffer = [_currentCore ringBufferAtIndex:0];
if(_currentCore->_audioBuffer)
{
[_currentCore->_audioBuffer setLength:(sizeof(INT16) * sampleInterval * 8)];
}
}
void BlissAudioMixer::release()
{
AudioMixer::release();
}
void BlissAudioMixer::flushAudio()
{
NSUInteger bytesPerSample = sizeof(INT16);
NSUInteger bytesToWrite = sampleCount * bytesPerSample;
[_currentCore->_bufferLock lock];
[_currentCore->_audioBuffer write:this->sampleBuffer maxLength:bytesToWrite];
[_currentCore->_bufferLock unlock];
// updates buffer write position and sample count
AudioMixer::flushAudio();
}
#pragma mark Bliss Video Bus
void BlissVideoBus::init(UINT32 width, UINT32 height)
{
VideoBus::init(width, height);
}
void BlissVideoBus::release()
{
if (_currentCore->_videoBuffer) {
delete[] _currentCore->_videoBuffer;
_currentCore->_videoBuffer = NULL;
}
VideoBus::release();
}
void BlissVideoBus::render()
{
VideoBus::render();
[_currentCore->_bufferLock lock];
memcpy(_currentCore->_videoBuffer, this->pixelBuffer, this->pixelBufferSize);
[_currentCore->_bufferLock unlock];
}
#pragma mark Bliss Input Producer
BlissInputProducer::BlissInputProducer()
: InputProducer((GUID){0})
{
}
float BlissInputProducer::getValue(INT32 enumeration)
{
BOOL isKeyboardDevice = this->isKeyboardDevice();
char player = this->player;
float value = 0.0f;
if(isKeyboardDevice)
{
uint64_t keyflag = INTY_TO_BITMAP(enumeration);
value = INTY_TEST(_keyboard, keyflag) == keyflag ? 1.0f : 0.0f;
}
else
{
if(enumeration >= CONTROLLER_DISC_DOWN && enumeration <= CONTROLLER_DISC_UP_LEFT)
{
value = INTY_TEST(_controller[player].disc, enumeration) == enumeration ? 1.0f : 0.0f;
}
else if(enumeration == CONTROLLER_ACTION_TOP || enumeration == CONTROLLER_ACTION_BOTTOM_LEFT || enumeration == CONTROLLER_ACTION_BOTTOM_RIGHT)
{
value = INTY_TEST(_controller[player].action, enumeration) == enumeration ? 1.0f : 0.0f;
}
else if(enumeration >= CONTROLLER_KEYPAD_THREE && enumeration <= CONTROLLER_KEYPAD_CLEAR)
{
value = INTY_TEST(_controller[player].keypad, enumeration) == enumeration ? 1.0f : 0.0f;
}
}
return value;
}
#pragma mark - OEIntellivisionSystemResponderClient
- (int)blissButtonForIntellivisionButton:(OEIntellivisionButton)button player:(NSUInteger)player;
{
int btn = -1;
static int OEBlissIntellivisionButton[] =
{
CONTROLLER_DISC_UP,
CONTROLLER_DISC_DOWN,
CONTROLLER_DISC_LEFT,
CONTROLLER_DISC_RIGHT,
CONTROLLER_ACTION_TOP,
CONTROLLER_ACTION_BOTTOM_LEFT,
CONTROLLER_ACTION_BOTTOM_RIGHT,
CONTROLLER_KEYPAD_ONE,
CONTROLLER_KEYPAD_TWO,
CONTROLLER_KEYPAD_THREE,
CONTROLLER_KEYPAD_FOUR,
CONTROLLER_KEYPAD_FIVE,
CONTROLLER_KEYPAD_SIX,
CONTROLLER_KEYPAD_SEVEN,
CONTROLLER_KEYPAD_EIGHT,
CONTROLLER_KEYPAD_NINE,
CONTROLLER_KEYPAD_ZERO,
CONTROLLER_KEYPAD_CLEAR,
CONTROLLER_KEYPAD_ENTER
};
if(button < OEIntellivisionButtonCount && button >= OEIntellivisionButtonUp)
{
btn = OEBlissIntellivisionButton[button];
}
return btn;
}
- (void)setIntellivisionButton:(int)btn isDown:(BOOL)down forPlayer:(NSUInteger)player
{
switch(btn)
{
case CONTROLLER_DISC_DOWN:
case CONTROLLER_DISC_RIGHT:
case CONTROLLER_DISC_UP:
case CONTROLLER_DISC_LEFT: {
_controller[player-1].disc = down ?
INTY_ON(_controller[player-1].disc, btn) :
INTY_OFF(_controller[player-1].disc, btn);
// if both horizontal + vertical disc directions are active,
// turn on the wide bit flag for 45-degree angles
if((_controller[player-1].disc & (CONTROLLER_DISC_LEFT|CONTROLLER_DISC_RIGHT))
&& (_controller[player-1].disc & (CONTROLLER_DISC_UP|CONTROLLER_DISC_DOWN)))
{
INTY_ON(_controller[player-1].disc, CONTROLLER_DISC_WIDE);
}
else
{
INTY_OFF(_controller[player-1].disc, CONTROLLER_DISC_WIDE);
}
break;
}
case CONTROLLER_KEYPAD_ONE:
case CONTROLLER_KEYPAD_TWO:
case CONTROLLER_KEYPAD_THREE:
case CONTROLLER_KEYPAD_FOUR:
case CONTROLLER_KEYPAD_FIVE:
case CONTROLLER_KEYPAD_SIX:
case CONTROLLER_KEYPAD_SEVEN:
case CONTROLLER_KEYPAD_EIGHT:
case CONTROLLER_KEYPAD_NINE:
case CONTROLLER_KEYPAD_CLEAR:
case CONTROLLER_KEYPAD_ZERO:
case CONTROLLER_KEYPAD_ENTER:
_controller[player-1].keypad = down ?
INTY_ON(_controller[player-1].keypad, btn) :
INTY_OFF(_controller[player-1].keypad, btn);
break;
case CONTROLLER_ACTION_TOP:
case CONTROLLER_ACTION_BOTTOM_LEFT:
case CONTROLLER_ACTION_BOTTOM_RIGHT:
_controller[player-1].action = down ?
INTY_ON(_controller[player-1].action, btn) :
INTY_OFF(_controller[player-1].action, btn);
break;
default: break;
}
}
- (oneway void)didPushIntellivisionButton:(OEIntellivisionButton)button forPlayer:(NSUInteger)player;
{
int btn = [self blissButtonForIntellivisionButton:button player:player];
if(btn > -1)
{
[self setIntellivisionButton:btn isDown:YES forPlayer:player];
}
}
- (oneway void)didReleaseIntellivisionButton:(OEIntellivisionButton)button forPlayer:(NSUInteger)player;
{
int btn = [self blissButtonForIntellivisionButton:button player:player];
if(btn > -1)
{
[self setIntellivisionButton:btn isDown:NO forPlayer:player];
}
}
- (int)intellivisionKeyForKeyCode:(unsigned short)keyCode
{
int btn = -1;
switch(keyCode)
{
default: break;
case kHIDUsage_KeyboardA: btn = ECS_KEYBOARD_A; break;
case kHIDUsage_KeyboardB: btn = ECS_KEYBOARD_B; break;
case kHIDUsage_KeyboardC: btn = ECS_KEYBOARD_C; break;
case kHIDUsage_KeyboardD: btn = ECS_KEYBOARD_D; break;
case kHIDUsage_KeyboardE: btn = ECS_KEYBOARD_E; break;
case kHIDUsage_KeyboardF: btn = ECS_KEYBOARD_F; break;
case kHIDUsage_KeyboardG: btn = ECS_KEYBOARD_G; break;
case kHIDUsage_KeyboardH: btn = ECS_KEYBOARD_H; break;
case kHIDUsage_KeyboardI: btn = ECS_KEYBOARD_I; break;
case kHIDUsage_KeyboardJ: btn = ECS_KEYBOARD_J; break;
case kHIDUsage_KeyboardK: btn = ECS_KEYBOARD_K; break;
case kHIDUsage_KeyboardL: btn = ECS_KEYBOARD_L; break;
case kHIDUsage_KeyboardM: btn = ECS_KEYBOARD_M; break;
case kHIDUsage_KeyboardN: btn = ECS_KEYBOARD_N; break;
case kHIDUsage_KeyboardO: btn = ECS_KEYBOARD_O; break;
case kHIDUsage_KeyboardP: btn = ECS_KEYBOARD_P; break;
case kHIDUsage_KeyboardQ: btn = ECS_KEYBOARD_Q; break;
case kHIDUsage_KeyboardR: btn = ECS_KEYBOARD_R; break;
case kHIDUsage_KeyboardS: btn = ECS_KEYBOARD_S; break;
case kHIDUsage_KeyboardT: btn = ECS_KEYBOARD_T; break;
case kHIDUsage_KeyboardU: btn = ECS_KEYBOARD_U; break;
case kHIDUsage_KeyboardV: btn = ECS_KEYBOARD_V; break;
case kHIDUsage_KeyboardW: btn = ECS_KEYBOARD_W; break;
case kHIDUsage_KeyboardX: btn = ECS_KEYBOARD_X; break;
case kHIDUsage_KeyboardY: btn = ECS_KEYBOARD_Y; break;
case kHIDUsage_KeyboardZ: btn = ECS_KEYBOARD_Z; break;
case kHIDUsage_Keyboard1: btn = ECS_KEYBOARD_1; break;
case kHIDUsage_Keyboard2: btn = ECS_KEYBOARD_2; break;
case kHIDUsage_Keyboard3: btn = ECS_KEYBOARD_3; break;
case kHIDUsage_Keyboard4: btn = ECS_KEYBOARD_4; break;
case kHIDUsage_Keyboard5: btn = ECS_KEYBOARD_5; break;
case kHIDUsage_Keyboard6: btn = ECS_KEYBOARD_6; break;
case kHIDUsage_Keyboard7: btn = ECS_KEYBOARD_7; break;
case kHIDUsage_Keyboard8: btn = ECS_KEYBOARD_8; break;
case kHIDUsage_Keyboard9: btn = ECS_KEYBOARD_9; break;
case kHIDUsage_Keyboard0: btn = ECS_KEYBOARD_0; break;
case kHIDUsage_KeyboardReturnOrEnter: btn = ECS_KEYBOARD_RETURN; break;
case kHIDUsage_KeyboardEscape: btn = ECS_KEYBOARD_ESCAPE; break;
case kHIDUsage_KeyboardDeleteOrBackspace: btn = ECS_KEYBOARD_LEFT; break;
case kHIDUsage_KeyboardSpacebar: btn = ECS_KEYBOARD_SPACE; break;
case kHIDUsage_KeyboardHyphen: btn = ECS_KEYBOARD_6; break; // shifted
case kHIDUsage_KeyboardEqualSign: btn = ECS_KEYBOARD_1; break; // shifted
case kHIDUsage_KeyboardSemicolon: btn = ECS_KEYBOARD_SEMICOLON; break;
case kHIDUsage_KeyboardQuote: btn = ECS_KEYBOARD_RIGHT; break; // shifted
case kHIDUsage_KeyboardComma: btn = ECS_KEYBOARD_COMMA; break;
case kHIDUsage_KeyboardPeriod: btn = ECS_KEYBOARD_PERIOD; break;
case kHIDUsage_KeyboardSlash: btn = ECS_KEYBOARD_7; break; // shifted
case kHIDUsage_KeyboardRightArrow: btn = ECS_KEYBOARD_RIGHT; break;
case kHIDUsage_KeyboardLeftArrow: btn = ECS_KEYBOARD_LEFT; break;
case kHIDUsage_KeyboardDownArrow: btn = ECS_KEYBOARD_DOWN; break;
case kHIDUsage_KeyboardUpArrow: btn = ECS_KEYBOARD_UP; break;
case kHIDUsage_KeyboardReturn: btn = ECS_KEYBOARD_RETURN; break;
case kHIDUsage_KeyboardLeftControl:
case kHIDUsage_KeyboardRightControl: btn = ECS_KEYBOARD_CONTROL; break;
case kHIDUsage_KeyboardLeftShift:
case kHIDUsage_KeyboardRightShift: btn = ECS_KEYBOARD_SHIFT; break;
}
return btn;
}
- (BOOL)isIntellivisionKeyShiftedForKeycode:(unsigned short)keyCode
{
switch(keyCode)
{
default: break;
case kHIDUsage_KeyboardLeftShift:
case kHIDUsage_KeyboardRightShift: return YES; break;
case kHIDUsage_KeyboardHyphen: return YES; break;
case kHIDUsage_KeyboardEqualSign: return YES; break;
case kHIDUsage_KeyboardQuote: return YES; break;
case kHIDUsage_KeyboardSlash: return YES; break;
}
return NO;
}
- (void)setIntellivisionKey:(int)key isDown:(BOOL)down isShifted:(BOOL)shifted
{
uint64_t shiftflag = INTY_TO_BITMAP(ECS_KEYBOARD_SHIFT);
// HACK: double re-map
if(_keyboardShiftCount > 0)
{
switch(key)
{
default: break;
case ECS_KEYBOARD_1: key = ECS_KEYBOARD_5; break;
case ECS_KEYBOARD_5: key = ECS_KEYBOARD_LEFT; break;
case ECS_KEYBOARD_6: key = ECS_KEYBOARD_UP; break;
case ECS_KEYBOARD_7: key = ECS_KEYBOARD_DOWN; break;
case ECS_KEYBOARD_RIGHT: key = ECS_KEYBOARD_2; break;
}
}
if(shifted)
{
if(down)
{
if(_keyboardShiftCount == 0)
{
INTY_ON(_keyboard, shiftflag);
}
_keyboardShiftCount++;
}
else
{
_keyboardShiftCount--;
if(_keyboardShiftCount == 0)
{
INTY_OFF(_keyboard, shiftflag);
}
}
//DLog(@"_keyboardShiftCount == %i", _keyboardShiftCount);
}
uint64_t keyflag = INTY_TO_BITMAP(key);
if(down)
{
INTY_ON(_keyboard, keyflag);
_keyboardDownCount++;
}
else
{
INTY_OFF(_keyboard, keyflag);
_keyboardDownCount--;
if(_keyboardDownCount == 0)
{
_keyboard = 0;
}
}
//DLog(@"_keyboardDownCount == %i", _keyboardDownCount);
}
- (oneway void)keyDown:(unsigned short)keyCode
{
int key = [self intellivisionKeyForKeyCode:keyCode];
if(key > -1)
{
BOOL shifted = [self isIntellivisionKeyShiftedForKeycode:keyCode];
[self setIntellivisionKey:key isDown:YES isShifted:shifted];
}
}
- (oneway void)keyUp:(unsigned short)keyCode
{
int key = [self intellivisionKeyForKeyCode:keyCode];
if(key > -1)
{
BOOL shifted = [self isIntellivisionKeyShiftedForKeycode:keyCode];
[self setIntellivisionKey:key isDown:NO isShifted:shifted];
}
}
@end