Cocoa Port pt 4 - Keyboard

We are in the home stretch now. After this session, the game will be playable. We need to write the code in the NSView to catch the keyboard events and pass them to the machine. In the machine code, we'll take those keyboard events and set the matching input port bit. The game's code running in the 8080 emulator is already polling the ports for keys, up to now it just doesn't see any.

Accepting key events in the machine

I'll add 2 methods to SpaceInvadersMachine.m, KeyDown and KeyUp. When KeyDown is called, set the corresponding bit in the input port. When KeyUp is called, clear the corresponding bit. The code looks a lot like we presented a few sections back:

   - (void) KeyDown: (uint8_t) key    
   {    
       switch (key)    
       {    
           case KEY_COIN:    
               in_port1 |= 0x1;    
               break;    
           case KEY_P1_LEFT:    
               in_port1 |= 0x20;    
               break;    
           case KEY_P1_RIGHT:    
               in_port1 |= 0x40;    
               break;    
        /*.... etc.....*/    
       }    
   }    


   - (void) KeyUp: (uint8_t) key    
   {    
       switch (key)    
       {    
           case KEY_COIN:    
               in_port1 &= ~0x1;    
               break;    
           case KEY_P1_LEFT:    
               in_port1 &= ~0x20;    
               break;    
        /*.... etc.....*/    
       }    
   }    


Getting the key events in the NSView

We should automatically get key events due to the way we built our project. If you don't see them, look into the "acceptFirstResponder" method. We just have to pass them along to the machine.

   - (void) keyDown: (NSEvent *) event    
   {    
       NSString *characters;    
       characters = [event characters];    

       unichar character;    
       character = [characters characterAtIndex: 0];    

       switch (character)    
       {    
           case NSLeftArrowFunctionKey:    
           case 'a':    
               [invaders KeyDown:KEY_P1_LEFT];    
               break;    

           case NSRightArrowFunctionKey:    
           case 's':    
               [invaders KeyDown:KEY_P1_RIGHT];    
               break;    
        /*.... etc.....*/    
       }    
   }    

   - (void) keyUp: (NSEvent *) event    
   {    
       NSString *characters;    
       characters = [event characters];    

       unichar character;    
       character = [characters characterAtIndex: 0];    

       switch (character)    
       {    
           case NSLeftArrowFunctionKey:    
           case 'a':    
               [invaders KeyUp:KEY_P1_LEFT];    
               break;    

           case NSRightArrowFunctionKey:    
           case 's':    
               [invaders KeyUp:KEY_P1_RIGHT];    
               break;    
        /*.... etc.....*/    
       }    
   }    

El crashola

Once I hooked this up and pressed "C" to put in a coin, I saw

   Error: Unimplemented instruction    
   0037 DAA    

Oh crap. I didn't think that DAA was necessary so I didn't implement it or the auxiliary carry (AC) flag. Let me look at the data book.... It says

The eight-bit number in the accumulator is adjusted to form two four-bit Binary-Coded-Decimal digits by the following Process:

  1. If the value of the least significant 4 bits of the accumulator is greater than 9 OR if the AC flag is set, 6 is added to the accumulator.

  2. If the value of the most significant 4 bits of the accumulator is now greater than 9, or if the CY flag is set, 6 is added to the most significant 4 bits of the accumulator.

I think all the game is going to do is convert the hex score to base-10 for display. I do not think it is going to do base-10 math, so I think I can get away with ignoring AC:

    case 0x27:                          //DAA    
        if ((state->a &0xf) > 9)    
            state->a += 6;    
        if ((state->a&0xf0) > 0x90)    
        {    
            uint16_t res = (uint16_t) state->a + 0x60;    
            state->a = res & 0xff;    
            ArithFlagsA(state, res);    
        }    
        break;    

I tried this and I haven't seen any problems with number display yet. I don't think Space Invaders is doing any BCD math, I think it keeps the score in hex and only converts it for display.

It Works!

What do you know - the game is playable! How great is that? The only thing left to do is the sound.

View my code with the keyboard handling in the github project under CocoaPart4-Keyboard

← Prev: debugging-tales   Next: cocoa-port-pt-5---sound →


Post questions or comments on Twitter @realemulator101, or if you find issues in the code, file them on the github repository.