iPhone Port pt 2 - Machine Object

The iPhone machine object is identical to the Cocoa object.

Stage one of the machine code is kind of long. I'm not putting every single line of code in this explanation - You might want to download the end code and examine it as you write yours.

   @interface SpaceInvadersMachine : NSObject    
   {    
       State8080   *state;    

       double      lastTimer;    
       double      nextInterrupt;    
       int         whichInterrupt;    

       NSTimer     *emulatorTimer;    

       uint8_t     shift0;         //LSB of Space Invader's external shift hardware    
       uint8_t     shift1;         //MSB    
       uint8_t     shift_offset;   //offset for external shift hardware    

   }    

   -(double) timeusec;    

   -(void) ReadFile:(NSString*)filename IntoMemoryAt:(uint32_t)memoffset;    
   -(id) init;    

   -(void) doCPU;    
   -(void) startEmulation;    

   -(void *) framebuffer;    

Since this object doesn't really do any dynamic allocations, I haven't used any of the @property or @synthesize features of the language.

Timer and interrupts

On a real CPU, instructions take different amounts of time to execute. On the older processors, this is pretty simple and is often given in the data book. For example, the LXI r, (word) command takes 3 cycles. I modified the emulator to return the cycles each instruction uses.

I used an NSTimer to get periodic cycles to run the emulator. The timer is not guaranteed to run at exact fixed intervals, it is only "about" and we have to account for that. The logic is, each time the timer is called:

  1. See exactly (down to the microsecond) how much time has elapsed since the last time the CPU ran.

  2. See if it is time for an interrupt. If it is, then make one happen.

  3. Run the CPU for that many cycles, assuming the CPU ran at 2MHz.

  4. Save the current microsecond time for next time.

This insures that the emulation will proceed at about 2MHz. If we execute Space Invaders too fast, the game spins waiting on the interrupt to happen. It doesn't seem to be that sensitive.

Ports

I made variables to store the value of each port to be read. To get the game to run in attract mode, we just need to return 1 from IN 0 and zero from IN 1. The external shift is implemented in the IN 3, OUT 2, and OUT 4 instructions as described in detail in the buttons section.

   -(uint8_t) InSpaceInvaders:(uint8_t) port    
   {    
       unsigned char a;    
       switch(port)    
       {    
           case 0:    
               return 1;    
           case 1:    
               return 0;    
           case 3:    
           {    
               uint16_t v = (shift1<<8) | shift0;    
               a = ((v >> (8-shift_offset)) & 0xff);    
           }    
               break;    
       }    
       return a;    
   }    

   -(void) OutSpaceInvaders:(uint8_t) port value:(uint8_t)value    
   {    
       switch(port)    
       {    
           case 2:    
               shift_offset = value & 0x7;    
               break;    
           case 4:    
               shift0 = shift1;    
               shift1 = value;    
               break;    
       }    

   }    

Display

The [SpaceInvadersMachine framebuffer] call returns the location of the video memory in the machine. The platform code needs it for drawing to the window.

Init

The initialization code will make a new 8080 emulator object and read the ROM file into its RAM. Since the ROM files will be copied into the application's app bundle by the Copy Phase of the build, we can find them by looking into the bundle. This code will read the "invaders.h" file into the the machine's memory at offset zero. (See the code for actual usage.)


       NSBundle *mainbundle = [NSBundle mainBundle];    
       NSString *filepath = [mainbundle pathForResource:@"invaders.h" ofType:NULL];    
       NSData *data = [[NSData alloc]initWithContentsOfFile:filepath];    

       uint8_t *buffer = &state->memory[0];    
       memcpy(buffer, [data bytes], [data length]);    

startEmulation

The startEmulation call starts the CPU running by putting a timer on the NSRunLoop.

The next section will describe the platform code that will put the image drawn by the game to the window. At the end of that section, we will be able to see the game running!

← Prev: iphone-port-pt-1---project-setup   Next: iphone-port-pt-3---viewcontroller.m →


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