My first fortnight with the Raspberry Pi

The 12th of June was a good day for me. For one thing, I received the second semester results for my course to find that I’d received A grades in all three subjects and come close to perfect in C programming. For another, my Toolkit, the diverse set of computers which I own, gained another member in a new Raspberry Pi, bringing my computer collection up to eight.

The Raspberry Pi, for those who don’t already know, is an ARM-based single-board computer with a 700MHz ARM11 processor, 256MB of RAM, a powerful GPU and a very low price tag, on the order of £25 for the Model B version which includes a 10/100 Ethernet port and two USB slots in comparison to the single USB slot on the yet-unreleased Model A. Demand has been staggering, and after several months of waiting, I finally had my hands on a Raspberry Pi of my own.

Unfortunately, my worry that I had either bought an SD card which didn’t work or that my Raspberry Pi might be faulty prevented the first boot from being as exciting as it should have been, but once I had realised my own mistakes and resolved them, I had another working Linux system in front of me, ready to test. A few hours were spent loading some of my preferred software which wasn’t loaded onto the Debian image by default, including Emacs, but the Debian Squeeze image gave a remarkably complete starting system with enough programming tools to get going almost immediately.

Having failed to locate my HDMI to DVI lead which would give me a bit more flexibility when it comes to monitor choice – I don’t own any monitors with HDMI support – I resorted to the composite connection, which I’ve set up to provide 640×480 support – enough to show 80 columns in text mode and to show a couple of windows using X. It’s not exceptional, and compared to the full 1080p performance that the HDMI slot can deliver, it’s really quite awkward, but the provision of the composite socket was a good idea by the Raspberry Pi Foundation, giving the Raspberry Pi the ability to run on a fairly wide arrangement of monitors and TV screens.

My collection of computers means that I’m not exactly short of Linux PCs, so after doing a brief test of the GCC compiler and Python interpreter, I decided to begin looking for resources to learn something which the Raspberry Pi is well-suited for – ARM assembly language. Perplexingly, while resources on learning ARM assembly language are reasonably available on the internet, most of the resources on learning Linux assembly language seem to be related to the x86 architecture – an architecture with much uglier assembly language, with far less user-available registers and a CISC model which means that there are operations to learn with more variance between them than on the ARM’s RISC model.

I have managed to find sufficient materials to make a start, though, so I’ve managed to make some headway into learning how the ARM processor works, which will hopefully give me a bit more insight into how the processor works and how code can be effectively optimised. As the Debian Squeeze image for the Raspberry Pi comes with GCC as default, as any good, full Linux distribution should, the appropriate assembler was already present, and there are, of course, a lot of technical details about the ARM architecture’s instruction set available from ARM themselves. All I can really do of my own accord so far is to do simple arithmetic or bitwise logical operations, but everybody has to start somewhere.

I haven’t really tested the performance of the Raspberry Pi to its full yet, but I do have a somewhat similar embedded system with broadly similar specifications in my hacked Wii console, which also runs Debian Linux with the same LXDE desktop environment as on my Raspberry Pi. The Raspberry Pi seems to run more smoothly than the Wii running Linux, possibly a consequence of the greater RAM. There are a few slowdowns when loading Emacs on X, but Emacs doesn’t even load instantaneously on my dual-core desktop, so the slight pause is something I’m willing to tolerate. Anyway, the greatest strength of the Raspberry Pi is meant to be in its GPU, and I haven’t tested that yet, so my call on the Raspberry Pi’s performance will be more complete when I have a more full picture. So far, though, I can’t complain – it should serve the educational audience well enough to fit the programming tasks which the Raspberry Pi Foundation seem to have at the front of their minds.

Advertisements

A Project with Source Code: Hexadecimal Little Man Computer Simulator

Author’s Note: This is just the result of a little experiment I was doing recently. This simulator uses a modified version of the Little Man educational computer instruction set, using hexadecimal byte encoding rather than binary-coded decimal, and as such is not particularly sophisticated or useful. As this code was written in a short period of time, it is not the cleanest, and there are several elements where the performance is sub-optimal; these include the switches, which flick on and off far too quickly when a key is pressed.

The simulator uses the Allegro library – much of what I was doing was simply for the purpose of getting to grips with the graphical functions, and it made some sense to adapt a previously tested program to test these out.

The key layout is as follows:

P – toggle simulator power

Q, W, E, R, T, Y, U, I – toggle address counter bits 0 to 7

A, S, D, F, G, H, J, K, Z, X, C, F – toggle address content bits 0 to 11

Enter – Execute instructions

Space – Enter input (only operational when Awaiting Input light is on)

Delete – Clear memory

ESC – Quit

The Little Man instruction set has nine operation codes as standard, with some additional instructions added as placeholders in the hexadecimal system. They are described below (where xx is a hexadecimal number):

1xx – Add the contents of memory address xx to the accumulator

2xx – Subtract the contents of memory address xx from the accumulator

3xx – Store the contents of the accumulator in memory address xx

5xx – Retrieve the contents of memory address xx and place them in the accumulator

6xx – Unconditional branch to memory address xx

7xx – Branch to memory address xx if the contents of the accumulator equal zero

8xx – Branch to memory address xx if the contents of the accumulator equal or are greater than zero

901 – Accept input, which will be placed in the accumulator (the user will be prompted to enter a number via the address contents switches)

902 – Output the contents of the accumulator (these will be printed using fprintf() to a file whose name by default is lmc_output, and is placed in the same directory as the executable file)

0xx – End program

4xx, 9xx (where xx >= 03), Axx, Bxx, Cxx, Dxx, Exx, Fxx – NOP (no operation)

Note: The layout of this blog format doesn’t extend to the 80 columns required to show all of the source code appropriately. You can circumvent this restriction by dragging the mouse over all of the source code and copying it to a text editor; the hidden code will show up.

/* lmc_gui: A graphical simulator for the Little Man educational instruction
   set, modified for hexadecimal operation.
   Copyright (C) 2012  Richard Kiernan

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include <allegro.h>
#include <stdio.h>

/* Hexadecimal LMC instruction set has 255 "mailboxes" */
#define MAILBOXES 0x100
#define INSTRUCTION_MAX 0xFFF /* Highest value of LMC instructions/data */
#define OP_SPLIT 0xFF /* Denotes the split between opcodes and operands */
#define INSTRUCTION_REST 15 /* The pause time between executed instructions
			     * in milliseconds */

/* Colour definitions */
#define OFF_RED makecol(148,64,64)
#define ON_RED makecol(225,64,64)
#define BG_BLUE makecol(78,191,89)
#define TOGGLE_YELLOW makecol(186,255,0)

/* Memory address structure */
struct memory_address {
    int address;
    int contents;
} mailbox[MAILBOXES];

/* Function prototypes */
void setup_screen(void);
void setup_components(void);
void setup_accumulator(void);
void setup_memory_addresses(void);
void setup_memory_contents(void);
void setup_power_switch(void);
void setup_clear_switch(void);
void setup_execute_switch(void);
void setup_input_light(void);
void initialise_memory(void);
void get_input(void);
void print_status(void);
void toggle_power(int setting);
void toggle_address_switch(int toggle, int setting);
void set_contents(void);
void toggle_contents_switch(int toggle, int setting, char mode);
void toggle_clear_switch(void);
void toggle_execute_switch(void);
void execute_instructions(void);
void set_accumulator(void);
void display_op_status(int opcode, int operand);
void get_program_input(void);

/* Program variables */
int pc = 0; /* Program counter */
int power = 0; /* Power status */
int accumulator = 0; /* Accumulator contents */
int input_temp = 0; /* Temporary storage for user input */

/* lmc_gui: A graphical simulator for the Little Man educational instruction
   set, modified for hexadecimal operation.
   Copyright (C) 2012  Richard Kiernan

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "lmc_gui.h"

int main(void)
{
    /* Initialise program */
    allegro_init();
    setup_screen();
    install_keyboard();
    initialise_memory();

    while (!key[KEY_ESC]) {
	/* Wait for a keypress */
	if (keypressed()) {
	    get_input();
	    rest(150);
	}
    }

    /* Clean up */
    allegro_exit();
    return 0;
}

/* setup_screen: Sets graphics mode, then calls the setup_components() function
   to draw the screen. */
void setup_screen() {
    /* Change video mode to 640x250 windowed */
    int ret = set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 250, 0, 0);
    if (ret != 0) {
	allegro_message(allegro_error);
	return;
    }

    setup_components();
}

/* setup_components: Sets up all the individual graphical elements of the
   simulator. */
void setup_components(void)
{
    rectfill(screen, 0, 0, SCREEN_W, SCREEN_H, 0); /* Clear screen */
    setup_accumulator();
    setup_memory_addresses();
    setup_memory_contents();
    setup_power_switch();
    setup_execute_switch();
    setup_clear_switch();
    setup_input_light();
}

/* setup_accumulator: Draws the accumulator lights in their default "off"
   state */
void setup_accumulator(void)
{
    int i;

    for (i = 0; i < 12; i++)
	circlefill(screen, 100 + i * 25, 50, 5, OFF_RED);

    textout_ex(screen, font, "ACCUMULATOR", 200, 60, 15, 0);
}

/* setup_memory_addresses: Draws the memory address lights and switches in their
   default "off" state */
void setup_memory_addresses(void)
{
    int i;

    textout_ex(screen, font, "MEMORY ADDRESS", 200, 85, 15, 0);
    for (i = 0; i < 8; i++)
	circlefill(screen, 200 + i * 25, 100, 5, OFF_RED);
    for (i = 0; i < 8; i++)
	rectfill(screen, 195 + i * 25, 120, 205 + i * 25, 140, BG_BLUE);
    for (i = 0; i < 8; i++)
	rectfill(screen, 195 + i * 25, 120, 205 + i * 25, 125, TOGGLE_YELLOW);
}

/* setup_memory_contents: Draws the address contents lights and switches in
   their default "off" state */
void setup_memory_contents(void)
{
    int i;

    textout_ex(screen, font, "ADDRESS CONTENTS", 200, 150, 15, 0);
    for (i = 0; i < 12; i++)
	circlefill(screen, 100 + i * 25, 165, 5, OFF_RED);
    for (i = 0; i < 12; i++)
	rectfill(screen, 95 + i * 25, 185, 105 + i * 25, 205, BG_BLUE);
    for (i = 0; i < 12; i++)
	rectfill(screen, 95 + i * 25, 185, 105 + i * 25, 190, TOGGLE_YELLOW);
}

/* setup_power_switch: Draws the power toggle light and switch in their default
   "off" state */
void setup_power_switch(void)
{
    circlefill(screen, 490, 50, 5, OFF_RED);
    rectfill(screen, 485, 65, 495, 85, BG_BLUE);
    rectfill(screen, 485, 65, 495, 70, TOGGLE_YELLOW);
    textout_ex(screen, font, "POWER", 500, 65, 15, 0);
}

/* setup_clear_switch: Draws the clear memory light and switch in their default
   "off" state */
void setup_clear_switch(void)
{
    circlefill(screen, 490, 100, 5, OFF_RED);
    rectfill(screen, 485, 120, 495, 140, BG_BLUE);
    rectfill(screen, 485, 120, 495, 125, TOGGLE_YELLOW);
    textout_ex(screen, font, "CLEAR", 500, 120, 15, 0);
    textout_ex(screen, font, "MEMORY", 500, 130, 15, 0);
}

/* setup_execute_switch: Draws the execute instructions light and switch in
   their default "off" state */
void setup_execute_switch(void)
{
    circlefill(screen, 490, 165, 5, OFF_RED);
    rectfill(screen, 485, 185, 495, 205, BG_BLUE);
    rectfill(screen, 485, 185, 495, 190, TOGGLE_YELLOW);
    textout_ex(screen, font, "EXECUTE", 500, 185, 15, 0);
    textout_ex(screen, font, "INSTRUCTIONS", 500, 195, 15, 0);
}

/* setup_input_light: Draws the input light in its default "off" state */
void setup_input_light(void)
{
    circlefill(screen, 425, 50, 5, OFF_RED);
    textout_ex(screen, font, "AWAITING", 400, 65, 15, 0);
    textout_ex(screen, font, "INPUT", 400, 75, 15, 0);
}

/* initialise_memory: zeroes the contents of all mailboxes and the
   accumulator */
void initialise_memory(void)
{
    int i;

    /* Set mailbox addresses appropriately, clear memory */
    for (i = 0; i < MAILBOXES; i++) {
	mailbox[i].address = i;
	mailbox[i].contents = 0;
    }

    /* Clear accumulator contents */
    accumulator = 0;
}

/* get_input: receives and resolves user input from the keyboard */
void get_input(void)
{
    /* Toggle power switch */
    if (key[KEY_P]) {
	if (!power)
	    toggle_power(1);
	else {
	    toggle_power(0);
	    initialise_memory();
	    setup_components();
	    pc = 0;
	}
    }

    /* Toggle address switches */
    if (key[KEY_Q] && power) {
	if (pc & (1 << 7))
	    toggle_address_switch(0, 0);
	else
	    toggle_address_switch(0, 1);
    }

    if (key[KEY_W] && power) {
	if (pc & (1 << 6))
	    toggle_address_switch(1, 0);
	else
	    toggle_address_switch(1, 1);
    }

    if (key[KEY_E] && power) {
	if (pc & (1 << 5))
	    toggle_address_switch(2, 0);
	else
	    toggle_address_switch(2, 1);
    }

    if (key[KEY_R] && power) {
	if (pc & (1 << 4))
	    toggle_address_switch(3, 0);
	else
	    toggle_address_switch(3, 1);
    }

    if (key[KEY_T] && power) {
	if (pc & (1 << 3))
	    toggle_address_switch(4, 0);
	else
	    toggle_address_switch(4, 1);
    }

    if (key[KEY_Y] && power) {
	if (pc & (1 << 2))
	    toggle_address_switch(5, 0);
	else
	    toggle_address_switch(5, 1);
    }

    if (key[KEY_U] && power) {
	if (pc & (1 << 1))
	    toggle_address_switch(6, 0);
	else
	    toggle_address_switch(6, 1);
    }

    if (key[KEY_I] && power) {
	if (pc & (1 << 0))
	    toggle_address_switch(7, 0);
	else
	    toggle_address_switch(7, 1);
    }

    /* Toggle contents switches */
    if (key[KEY_A] && power) {
	if (mailbox[pc].contents & (1 << 11))
	    toggle_contents_switch(0, 0, 't');
	else
	    toggle_contents_switch(0, 1, 't');
    }

    if (key[KEY_S] && power) {
	if (mailbox[pc].contents & (1 << 10))
	    toggle_contents_switch(1, 0, 't');
	else
	    toggle_contents_switch(1, 1, 't');
    }

    if (key[KEY_D] && power) {
	if (mailbox[pc].contents & (1 << 9))
	    toggle_contents_switch(2, 0, 't');
	else
	    toggle_contents_switch(2, 1, 't');
    }

    if (key[KEY_F] && power) {
	if (mailbox[pc].contents & (1 << 8))
	    toggle_contents_switch(3, 0, 't');
	else
	    toggle_contents_switch(3, 1, 't');
    }

    if (key[KEY_G] && power) {
	if (mailbox[pc].contents & (1 << 7))
	    toggle_contents_switch(4, 0, 't');
	else
	    toggle_contents_switch(4, 1, 't');
    }

    if (key[KEY_H] && power) {
	if (mailbox[pc].contents & (1 << 6))
	    toggle_contents_switch(5, 0, 't');
	else
	    toggle_contents_switch(5, 1, 't');
    }

    if (key[KEY_J] && power) {
	if (mailbox[pc].contents & (1 << 5))
	    toggle_contents_switch(6, 0, 't');
	else
	    toggle_contents_switch(6, 1, 't');
    }

    if (key[KEY_K] && power) {
	if (mailbox[pc].contents & (1 << 4))
	    toggle_contents_switch(7, 0, 't');
	else
	    toggle_contents_switch(7, 1, 't');
    }

    if (key[KEY_Z] && power) {
	if (mailbox[pc].contents & (1 << 3))
	    toggle_contents_switch(8, 0, 't');
	else
	    toggle_contents_switch(8, 1, 't');
    }

    if (key[KEY_X] && power) {
	if (mailbox[pc].contents & (1 << 2))
	    toggle_contents_switch(9, 0, 't');
	else
	    toggle_contents_switch(9, 1, 't');
    }

    if (key[KEY_C] && power) {
	if (mailbox[pc].contents & (1 << 1))
	    toggle_contents_switch(10, 0, 't');
	else
	    toggle_contents_switch(10, 1, 't');
    }

    if (key[KEY_V] && power) {
	if (mailbox[pc].contents & (1 << 0))
	    toggle_contents_switch(11, 0, 't');
	else
	    toggle_contents_switch(11, 1, 't');
    }

    /* Toggle clear memory switch */
    if (key[KEY_DEL] && power) {
	toggle_clear_switch();
    }

    /* Toggle execute instructions switch */
    if (key[KEY_ENTER] && power) {
	toggle_execute_switch();
    }
}

/* toggle_power: toggles power from on to off and vice versa; flips the switch
   and light status appropriately. */
void toggle_power(int setting)
{
    if (setting == 1) {
	power = 1;
	/* Turn on light, flip switch down */
	circlefill(screen, 490, 50, 5, ON_RED);
	rectfill(screen, 485, 65, 495, 85, BG_BLUE);
	rectfill(screen, 485, 80, 495, 85, TOGGLE_YELLOW);
    } else if (setting == 0) {
	power = 0;
	/* Turn off light, flip switch up */
	circlefill(screen, 490, 50, 5, OFF_RED);
	rectfill(screen, 485, 65, 495, 85, BG_BLUE);
	rectfill(screen, 485, 65, 495, 70, TOGGLE_YELLOW);
    }
}

/* toggle_address_switch: toggles the bit in the current address addressed by
 the switch from on to off and vice versa; flips the switch and light status
 appropriately. */
void toggle_address_switch(int toggle, int setting)
{
    /* Flip the bit of the program counter corresponding to the big-endian
       position of the address switch */
    pc ^= 1 << (7 - toggle);

    /* Change light setting, flip switch */
    if (setting == 1) {
	circlefill(screen, 200 + toggle * 25, 100, 5, ON_RED);
    	rectfill(screen, 195 + toggle * 25, 120, 205 + toggle * 25, 140,
		 BG_BLUE);
    	rectfill(screen, 195 + toggle * 25, 135, 205 + toggle * 25, 140,
		 TOGGLE_YELLOW);
    } else if (setting == 0) {
	circlefill(screen, 200 + toggle * 25, 100, 5, OFF_RED);
    	rectfill(screen, 195 + toggle * 25, 120, 205 + toggle * 25, 140,
		 BG_BLUE);
    	rectfill(screen, 195 + toggle * 25, 120, 205 + toggle * 25, 125,
		 TOGGLE_YELLOW);
    }

    set_contents();
}

/* set_contents: Sets the positions of the memory contents switches correctly
   based on the current memory address */
void set_contents(void)
{
    int i;

    /* Toggle switches and lights depending on contents of memory address */
    for (i = 0; i < 12; i++) {
	toggle_contents_switch(i, (mailbox[pc].contents
				   & (1 << (11 - i))) >> (11 - i), 's');
    }
}

/* toggle_contents_switch: Flips the switch and light status of the switch
 appropriately; depending on the mode, it may also toggle the bit in either
 the current memory address's contents or the temporary input storage buffer
 based on the switch. */
void toggle_contents_switch(int toggle, int setting, char mode)
{
    /* If mode is 't', flip the bit in the address contents corresponding to
       the big-endian position of the switch.
       If mode is 'a', do this for the bit in the temporary input store. */
    if (mode == 't')
	mailbox[pc].contents ^= 1 << (11 - toggle);
    if (mode == 'a')
	input_temp ^= 1 << (11 - toggle);

    /* Change light settings, flip switch */
    if (setting == 1) {
	circlefill(screen, 100 + toggle * 25, 165, 5, ON_RED);
	rectfill(screen, 95 + toggle * 25, 185, 105 + toggle * 25, 205,
		 BG_BLUE);
	rectfill(screen, 95 + toggle * 25, 200, 105 + toggle * 25, 205,
		 TOGGLE_YELLOW);
    } else if (setting == 0) {
	circlefill(screen, 100 + toggle * 25, 165, 5, OFF_RED);
	rectfill(screen, 95 + toggle * 25, 185, 105 + toggle * 25, 205,
		 BG_BLUE);
	rectfill(screen, 95 + toggle * 25, 185, 105 + toggle * 25, 190,
		 TOGGLE_YELLOW);
    }
}

/* toggle_clear_switch: Clears the contents of the mailbox contents; flips the
   clear switch up and down and turns the light on and off. */
void toggle_clear_switch(void)
{
    int i;

    /* Turn on light, flip switch down. Pause for effect */
    circlefill(screen, 490, 100, 5, ON_RED);
    rectfill(screen, 485, 120, 495, 140, BG_BLUE);
    rectfill(screen, 485, 135, 495, 140, TOGGLE_YELLOW);
    rest(100);

    /* Clear contents of all mailboxes */
    for (i = 0; i < MAILBOXES; i++) { 	
        mailbox[i].contents = 0;
    }

    /* Change positions of address contents switches */
    set_contents(); 
    /* Turn off light, flip switch up */
    circlefill(screen, 490, 100, 5, OFF_RED);
    rectfill(screen, 485, 120, 495, 140, BG_BLUE);
    rectfill(screen, 485, 120, 495, 125, TOGGLE_YELLOW); 
} 

/* toggle_execute_switch: Calls the execute_instructions() function; keeps the
   switch flipped down and the light turned on until that function has
   finished. */ 
void toggle_execute_switch(void)
{
    /* Turn on light, flip switch down. Pause for effect */
    circlefill(screen, 490, 165, 5, ON_RED);
    rectfill(screen, 485, 185, 495, 205, BG_BLUE);
    rectfill(screen, 485, 200, 495, 205, TOGGLE_YELLOW);
    rest(100);
    execute_instructions();

    /* Turn off light, flip switch up. */
    circlefill(screen, 490, 165, 5, OFF_RED);
    rectfill(screen, 485, 185, 495, 205, BG_BLUE);
    rectfill(screen, 485, 185, 495, 190, TOGGLE_YELLOW);
} 

/* execute_instructions: Splits instructions into op codes and operands, then
   executes those instructions in accordance with the modified hexadecimal LMC
   rules of execution. */ 
void execute_instructions(void) 
{
    int opcode = 0, operand = 0;
    int counter = 0;
    int i;
    FILE *ofp = fopen("lmc_output", "a");
    accumulator = 0;

    do {
        opcode = mailbox[counter].contents / (OP_SPLIT + 1);
        operand = mailbox[counter].contents & OP_SPLIT;
        switch(opcode) {
        case 0x1:
            accumulator += mailbox[operand].contents;
            break;
        case 0x2:
            accumulator -= mailbox[operand].contents;
            break;
        case 0x3:
            mailbox[operand].contents = accumulator;
            break;
        case 0x4:
            break; /* Treat as NOP */
        case 0x5:
            accumulator = mailbox[operand].contents;
            break;
        case 0x6:
            counter = operand;
            break;
        case 0x7:
        /* If accumulator is zero, branch; otherwise, progress to next
        instruction - treat as NOP. */
            if (accumulator == 0)
                counter = operand;
            else
                ++counter;
            break;
        case 0x8:
        /* If accumulator is greater than or equal to zero, branch;
           otherwise, progress to next instruction - treat as NOP. */
            if (accumulator >= 0)
                counter = operand;
	    else
		++counter;
	    break;
	case 0x9:
	    if (operand == 0x1) {
		/* Clear all contents switches */
		for (i = 0; i < 12; i++)
		    toggle_contents_switch(i, 0, 's');
		/* Turn on input light */
		circlefill(screen, 425, 50, 5, ON_RED);
		get_program_input();
		/* Turn off input light */
		circlefill(screen, 425, 50, 5, OFF_RED);
		set_contents(); /* Restore memory contents switch settings */
		accumulator = input_temp;
	    } else if (operand == 0x2) {
		fprintf(ofp, "%3hx\n", accumulator);
	    }
	    break;
	case 0x0:
	    set_accumulator(); /* Display accumulator contents */
	    fprintf(ofp, "----- INSTRUCTIONS COMPLETE -----\n");
	    fclose(ofp);
	    return;
	default:
	    break; /* All op codes outside of 0 - 9 are undefined.
		    * Treat as NOP. */
	}
	/* If not a jump instruction, increment the counter */
	if (opcode != 0x6 && opcode != 0x7 && opcode != 0x8)
	    ++counter;

	set_accumulator(); /* Display accumulator contents */

	/* Pause for effect, to enable accumulator lights to be seen in action.
	   May be changed or removed for a faster or slower simulation. */
	rest(INSTRUCTION_REST);

	if (key[KEY_ESC])
	    return; /* Allow the user to exit, e.g. from an infinite loop */
    } while (opcode != 0);
}

/* set_accumulator: Set the status of the accumulator lights based on the
   active bits in the accumulator "register" */
void set_accumulator(void)
{
    int i;

    /* Set accumulator lights based on the active bits in the accumulator */
    for (i = 0; i < 12; i++) {
	if (accumulator & 1 << (11 - i))
	    circlefill(screen, 100 + i * 25, 50, 5, ON_RED);
	else
	    circlefill(screen, 100 + i * 25, 50, 5, OFF_RED);
    }
}

/* get_program_input: Allows entry of data into the temporary input storage
   buffer using the memory contents switches. */
void get_program_input(void)
{
    /* Clear temporary input storage buffer */
    input_temp = 0;
    while (!key[KEY_SPACE]) {
	/* Toggle contents switches */
	if (key[KEY_A]) {
	    if (input_temp & (1 << 11))
		toggle_contents_switch(0, 0, 'a');
	    else
		toggle_contents_switch(0, 1, 'a');
	}

	if (key[KEY_S]) {
	    if (input_temp & (1 << 10))
		toggle_contents_switch(1, 0, 'a');
	    else
		toggle_contents_switch(1, 1, 'a');
	}

	if (key[KEY_D]) {
	    if (input_temp & (1 << 9))
		toggle_contents_switch(2, 0, 'a');
	    else
		toggle_contents_switch(2, 1, 'a');
	}

	if (key[KEY_F]) {
	    if (input_temp & (1 << 8))
		toggle_contents_switch(3, 0, 'a');
	    else
		toggle_contents_switch(3, 1, 'a');
	}

	if (key[KEY_G]) {
	    if (input_temp & (1 << 7))
		toggle_contents_switch(4, 0, 'a');
	    else
		toggle_contents_switch(4, 1, 'a');
	}

	if (key[KEY_H]) {
	    if (input_temp & (1 << 6))
		toggle_contents_switch(5, 0, 'a');
	    else
		toggle_contents_switch(5, 1, 'a');
	}

	if (key[KEY_J]) {
	    if (input_temp & (1 << 5))
		toggle_contents_switch(6, 0, 'a');
	    else
		toggle_contents_switch(6, 1, 'a');
	}

	if (key[KEY_K]) {
	    if (input_temp & (1 << 4))
		toggle_contents_switch(7, 0, 'a');
	    else
		toggle_contents_switch(7, 1, 'a');
	}

	if (key[KEY_Z]) {
	    if (input_temp & (1 << 3))
		toggle_contents_switch(8, 0, 'a');
	    else
		toggle_contents_switch(8, 1, 'a');
	}

	if (key[KEY_X]) {
	    if (input_temp & (1 << 2))
		toggle_contents_switch(9, 0, 'a');
	    else
		toggle_contents_switch(9, 1, 'a');
	}

	if (key[KEY_C]) {
	    if (input_temp & (1 << 1))
		toggle_contents_switch(10, 0, 'a');
	    else
		toggle_contents_switch(10, 1, 'a');
	}

	if (key[KEY_V]) {
	    if (input_temp & (1 << 0))
		toggle_contents_switch(11, 0, 'a');
	    else
		toggle_contents_switch(11, 1, 'a');
	}
    }
}