'font-family: courier monospace;' ); require_once('../_includes/include.php'); myHeader('Notes on apple2: An Apple ][ emulator'); ?>
I spent some time hacking at the code of apple2, an Apple ][ emulator. The first order of business was to acquaint myself with the code. Here are some (very) rough notes I took while surveying the source code.

Interface Commands

Various

The emulator defaults to ][e emulation there is a #define for this in the source code, but the emulator doesn't work when you change this. You CAN change the emulation to ][+. You just can't default to ][+ yet.

Disk Operations

You have two disk drives, labeled A and B.

Pressing either will give you a menu of the disks you can insert into your drives. By using '..' you can navigate your hard drive looking for disks. You'll see letters next to some of the disk images. Here's what they mean:

The interface is a little unintuitive.

Why do some disks don't work?

Many games in real life only worked on //e, since they needed it's extended 80 column features. And some poorly-designed ][+ programs falter on the //e.

Programs shouldn't fail on ][+ undocumented if they work on ][+, though. The latter mode basically adds an "Illegal instruction" exception, something no real Apple ever had. It's more for detecting bad code (some retrocomputing enthusiasts might be developing new Apple code on the emulator) than for practical use.

video.c

void video_plotchar( int row, int col, int color, unsigned char character );

This function prints a single character at location (row,col) using the following codes for color:

This function does NOT write to Apple memory--meaning, this function is to be used by the interface screens of the emulator, not the emulation itself.

interface.c

void c_interface_print( int row, int col, int color, unsigned char *string );

Makes use of video_plotchar to print string at (row, col) using color. Used only by the emulator's interface with the user. See video.cttvideo_plotchar for color codes.

void c_interface_redo_diskette_bottom( void );

Should be declared with void arg. This is called to repaint the bottom of the screen for the disk selection interface (F1, F2), usually when an error message is printed like trouble gunzipping, writing to write protected disk, etc. This doesn't paint the bottom at the outset. See c_interface_select_diskette for that.

void c_interface_select_diskette( int drive );

This function handles disk selection (pressing F1, F2). The argument here is obvious; 0=drive A, 1=drive B.

The principles of emulation are fairly straight forward. It's basically just:

extern char (*read_memory_functions)(int effective_address)[65536];
extern void (*write_memory_functions)(int effective_address,char data)[65536];
extern void (*opcode_functions)(void)[256];
extern struct registers regs;

int do_emulation(void)
{
   while (1) {
      int opcode;
      opcode = (*read_memory_functions[regs.program_counter])
               (regs.program_counter);
      (*opcode_functions)[opcode]();
   }
}

Note that apple2 does not follow this strictly--the real code is in assembler. This is simply an informative discussion on emulation in general.

The regs variable is the emulated computer's simulated CPU registers. Registers are built-in memory locations in the CPU. Since they're built right onto the CPU, they are extremely fast. These simulated registers are represented by a struct which contains several member variables, one of which is named program_counter. A program counter (abbreviated pc) keeps track of which machine code is currently being executed.

The program counter is used to access an array called read_memory_functions, which contains the Apple ][ program codes which contains accessor functions for each of the 64kcells of Apple memory. The reason a different function is used for each cell is that while much of a computers address space is just memory that stores a byte and then keeps giving back that byte until the next store, many cells are "magic" and do unusual things. For example, any access to $C050 causes an Apple II to switch to graphics mode, while incoming keypresses are read from $C000. Also, on the //e, all the `real' memory is bank-switched.

The first 32 bits of the code is copied to the variable opcode. This variable is used to execute a function pointed to by the function pointer array opcode_functions, each element containing the native platform equivalent code of the Apple ]['s code; it analyzes the code in the opcode variable and uses it to execute an equivalent native subroutine.

Most other Apple emulators use a bunch of if statements rather than an array. The array model is simplest, and fastest (except possibily for cache problems) but takes a lot of memory.

The hardest part is writing the codes to analyze the opcodes and execute an equivalent code. Something like x86 is a pain to work with because the opcode length is different from one another, some instructions you can use only with certain registers, etc. Something like MIPS should be relatively easy to write an emulator for because each instruction is exactly 32 bits long, any instruction can be used with any register, etc.

The code can be made faster if you analyze the entire set of opcodes first, write out the opcodes in another part of the memory, then just execute that part of the memory. The Nestra Nintendo emulator does just that.