The Unexpected Satellite – Part 1

Editor’s Note: This is the first piece of fictional writing that I’ve done in about two years. I don’t regard myself as a particularly good fiction writer (although, to be fair, I’m not fantastic at non-fiction either), but I wanted to file off some of the rust and see whether I could bring a science fiction story with relatively hard scientific elements to completion. Any constructive criticism would be appreciated.

It was difficult enough at the best of times to make your way through a military spacecraft, Alan Hargreaves thought as he staggered through the corridors of the ECSA Chronos. At about 175 centimetres in height, Alan was at the upper reaches of what would be considered “comfortable” on a spacecraft, which meant that he only occasionally had to bow his head to get under the various pipes and conduits protruding from the ceiling. At this moment, though, he was being further impeded by a large collection of various electronic components lying on the floors in piles against the walls, causing Alan to have to take a rather more winding path to his destination than he would have preferred.

After completing his Master’s degree in aerospace engineering, Alan had promptly joined the European Commonwealth Space Agency, the quasi-military wing of the European Commonwealth which controlled the Commonwealth’s dealings in space. In their service, he had quickly progressed up the technical ranks to become a Chief Technical Specialist by the age of twenty-nine. In truth, Alan spent more time as a precision mechanic than he did as the sort of applied mathematician he had trained as, but the pay was respectable and Alan didn’t mind the hands-on approach.

By now, Alan was thirty-three years old and had spent ten years in the job. He had long been regarded a candidate for fast-tracking into the position of Commander on board one of the ECSA’s military spacecraft, even before the notable incident three years earlier where a group of rogue Chinese taikonauts had attempted to cause an international incident by firing on the ECSA Hephaestus. Along with the rest of the crew and some unsung sensor observers on a far-flung ECSA space station, he had conducted his actions with composure. This composure had brought him to the attention of his superiors even as the incident had been swept under the carpet and explained to the general public as a mistake in a training exercise.

Officially, Alan was still assigned to the Hephaestus, although right now it was laid up for repairs after an unfortunate collision with a poorly-piloted civilian shuttle. Alan had not officially been placed on leave, though, so when his current mission had been announced with the requirement for an experienced set of technical staff, Alan had been temporarily reassigned to the Chronos for the mission.

As Alan was sidestepping one of the piles of components, a door opened up to his left-hand side, distracting Alan sufficiently to trip into another pile located in front of him. As he struggled to keep himself upright, a figure exclaimed through the door, “Hey, watch out for the equipment!”

Alan recognised the voice as that of Andrew Donovan, a computer expert also temporarily reassigned to the Chronos. After ensuring his footing, Alan replied, “Oh, hey, Andy. Sorry about that. You distracted me a bit.”

Andrew walked over to the pile of components and looked closely at them, before replying, “Just hope they’re not damaged. If they are, the Commonwealth will have my head!”

Don’t worry about it, Andy. If they’re designed for spacecraft use, I’m sure they can take a gentle boot tap every so often,” Alan replied. “Actually, I was going to ask you: Did you really have to bring the entire set of computers for that spacecraft along with you?”

The spacecraft that Alan was referring to was an asteroid mining vessel, designed to automate the laborious, repetitive task of pulverising asteroids and collecting the loose material for processing for metals rarely found on Earth. Normally, the system worked well, operating more quickly and cheaply than placing a human into the spacecraft for control purposes. This time, however, things had gone wrong, and the asteroid miner in question had dropped off the sensors somewhere in the Mars-Jupiter asteroid belt, not giving off any radio signals, nor any infra-red traces.

Well, the spacecraft did cost Brahms Space Mining eighteen billion euro,” Andrew replied after some consideration, “and it’s got a cargo on board projected to be worth one hundred million euro, some of which is earmarked for the Commonwealth itself. I felt that it was better to be safe than sorry.”

Alan grunted. “Right, of course. Well, if you can, try to keep the piles on one side or another. I mean, we’re used to being cramped in the ECSA, but we prefer not to have to walk like crabs to get from one place to another.”

Andrew nodded. “Understood, Alan. I’d best be off now anyway – I don’t think the Commander will want to wait too long. I’ll try to sort things out a bit more when I’m done.”

Alan grunted again, replying, “That’ll have to do. It’s not like we have any free space around anyway.”

The two men shared a brief, firm handshake, before saluting each other and progressing onwards in opposite directions. As Alan continued towards the maintenance bay, still winding his way through the assorted electronic equipment, he wondered if Andrew had underestimated the amount of space available on an ECSA military spacecraft, or if he was simply more comfortable with cramped space than Alan himself.

* * * * * * * * * *

Before long, Alan had settled back into his job. At that moment, he was resolving an incident that had occurred with a couple of the maintenance robots earlier. The robots in question, marked clearly in white paint with the serial numbers C11 and A15, had collided during a seemingly routine set of maintenance instructions, necessitating one of Alan’s technical assistants to climb through the tight maintenance pipes to clear the blockage before any other robots got clogged up in the same pipe junction.

Alan began looking closely at the robot marked C11. This robot, which went up to Alan’s waist, was shaped in the form of a rough sphere, with two rotating electromagnets used to “crawl” through the pipes and a set of optical sensors fitted around the two axes of the sphere. There also happened to be a big dent on one side of the robot, presumably from the collision. Alan thought of the lengthy repair job that he would have to do before the robot would be once again fit for service and sighed. First of all, he thought, he would have to figure out the cause of the collision.

Alan walked over to a nearby desk, picking up a large tablet computer and a diagnostic cable. Walking back to C11, he crouched down beside it, looked for the port for the cable and plugged the cable into the port, placing the other end into the tablet computer. Using the tablet, he loaded the command program which had been assigned to the robot and the sensor readouts onto the screen and took a cursory look at them. His quick glance didn’t reveal any glaring problems, so Alan proceeded over to another robot of similar construction which was sitting ready underneath an entry pipe into the maintenance network.

Shortly after loading the control program onto the undamaged robot, the robot pulled its way into the maintenance network, soon disappearing out of sight. Pulling up a few windows on his tablet, Alan aligned a view of the maintenance robots already in the network alongside a couple of views of the control program in both symbolic and text-based source code formats. After submitting a few commands to keep the other maintenance robots out of the path of his test, Alan sat down and watched the path of the robot as it made its way through the pipe network.

As the robot turned its way through each bend, Alan compared each turn with the corresponding instruction in the control program. Each time, the movement corresponded exactly to what was expected, and after twenty minutes, Alan was no closer to figuring out what had caused the collision. If the problem was with C11, Alan faced a slog through near-incomprehensible sensor readouts and a laborious examination of multiple components to find the problem. Before trying that, though, Alan thought that it would be better to test A15 first to either confirm or eliminate it as an obvious cause.

A15 looked somewhat similar to C11 in that it was spherical and contained electromagnets as a propulsion system, but on A15, there were two sliding hatches which contained a modular array of precision tools for repairing everything from cracks in the pipe network to miniscule electronic components. The optical sensors were also of a different model and arranged differently to the ones on C11.

As Alan was about to plug the diagnostic cable into the port on A15, he was disturbed by a tone from his earpiece. He looked down at his wrist and the screen of his wrist computer to see that he was being called by the Commander. Pressing down on the screen on his wrist, Alan answered, “Yes, Commander. Alan speaking.”

Hello, Alan,” the Commander replied. “Are you in the middle of something?”

Nothing that can’t wait,” Alan responded. “Just investigating a robot collision in the maintenance network. Routine work, although it looks like one of the robots took a heavy smack. It’ll be a bit of a job to fix it.”

Right. Anyway, I want you to come up to my office. Andrew was going through the details of the repair job on the asteroid miner, and he says that he needs a second man for the job.”

And I suppose then that I’m the second man?”

That would be correct. We need to discuss this plan in detail before our arrival.”

Yes, Commander. I’ll be up shortly.”

Pulling the diagnostic cable from its port on the tablet computer, Alan laid the tablet back in its place on the desk before walking over to the door of the maintenance bay and waiting for it to slide open. He had a feeling that he was going to have to spend quite a while later on figuring out the cause of the robotic collision, but for now, a more important task was at hand.

* * * * * * * * * *

A few minutes later, Alan was standing around a desk along with the Commander and Andrew. On the desk, there was a computer display of the blueprints for the asteroid miner, with a few areas of interest highlighted on the screen. Alan had studied the blueprints extensively before the journey, as, presumably, had Andrew, so the blueprints were there more for the benefit of the Commander, whose role was more to supervise the running of the Chronos and ensure that the crew got to their destination and back safely.

Alright, so you understand the plan, Alan?”, the Commander asked as Alan took a closer look at some of the dimension markings.

Pretty much, yes. We fly the shuttle over to the asteroid miner, enter through this entrance hatch,” Alan replied, pointing on the blueprints to a small hatch in the side of the spacecraft, “then replace the computer systems, the communications array, the power generators and anything else that could have been damaged.”

That’s the gist of it, yes,” the Commander replied.

So, just a standard repair job then. I’m sure that any of the technical staff on the ship could do it, given enough time. I’m just wondering what role I play in all of this. I mean, I don’t mind being transferred over from the Hephaestus; all I would have done is sit down and twiddle my thumbs until the repairs on my craft are finished. Still, there has to be a reason for having your own Chief Technical Specialist sit out of an apparently routine mission.”

Yes, I believe you were picked for a reason,” the Commander replied. “We could only acquire a two-person shuttle from Station Vienna. Neither I nor the Vice-Commander were trained as maintenance technicians.”

Alan raised his eyebrows in surprise, then replied, “Well, things are starting to make sense then.”

Andrew raised his hand timidly, and as the two other men turned their heads towards him, he asked, “Forgive me for interrupting, but I’m not sure I understand.”

Alan laughed. “It’s nothing, really. I’m guessing I’m the only maintenance technician on board who is officially rated to fly the shuttle in question. I can’t fly particularly well, though – I need to make that clear from the start. All I can promise is that I probably won’t fly into the side of the asteroid miner.” He paused for a second. “Probably.”

A Project With Source Code: An Updated Graphical, Hexadecimal LMC Simulator

Author’s Note: There were several details of my original simulator which I was dissatisfied with, even if the whole package did manage to properly simulate the hexadecimal variant of the LMC source code that it was meant to. The vector graphics were primitive, the toggle switches were far too responsive to extended key presses for my liking and the peculiar 640×250 resolution prevented the program from being easily made fullscreen. What’s more, each of the buttons and LEDs were placed based on “magic numbers” which were hardcoded into the source. None of these problems proved insurmountable, and I have now developed a slightly more sophisticated version of the simulator with bitmap graphics and switches which have a delay timer built into them.

The project still isn’t perfect; the appearance of the graphics is still amateur, although more appealing than the original version, and it doesn’t exactly mimic the operation of a comparable real-world machine, like the PDP-8 – to do that, I’d need to change the operation towards having a “load address” switch rather than having the switch positions change every time a new address is loaded. This wouldn’t be an insurmountable prospect either, although I prefer the switches being in sync with the LEDs for aesthetic reasons. A real-world LMC computer doesn’t exist, so I don’t have to worry about complete historical accuracy.

The controls are as they were in my original simulator, which is still available under the GNU General Public Licence as before. You will also require a number of graphics for the front plate, the LEDs and the switches. You can either use the graphics here, which you’ll have to manually convert to PCX format, or create your own, as long as they conform to the 640x250px size of the front plate, the 20x20px size of the LEDs and the 20x35px size of the toggle switches.

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>
#include <time.h>

#define MAILBOXES 0x100 /* Hexadecimal LMC instruction set has 255 */
			/* "mailboxes" */
#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 */

#define LED_WIDTH 20 /* LEDs are 20 pixels wide, 20 pixels high */
#define LED_HEIGHT 20
#define SWITCH_WIDTH 20 /* Switches are 20 pixels wide, 35 pixels high */
#define SWITCH_HEIGHT 35

#define BUFFER_HEIGHT 115 /* The black "buffer" on the top and bottom of the
			   screen is 115 pixels high. */

/* We want milliseconds, not full seconds! */
#define CLOCKS_PER_MS (CLOCKS_PER_SEC / 1000)

/* LED structure */
struct individual_led {
    int light_x, light_y;
    int activity_status;
};

/* LED/Switch combination structure */
struct led_switch {
    int light_x, light_y;
    int switch_x, switch_y;
    int activity_status;
    clock_t last_access_time;
};

/* Function prototypes */
void setup_screen(void);
void setup_structs(void);
void setup_components(void);
void setup_front_plate(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 setup_uinput_switch(void);
void initialise_memory(void);
void get_input(void);
void toggle_power(void);
void toggle_address_switch(int toggle);
void set_contents(void);
void toggle_contents_switch(int toggle, char mode);
void toggle_clear_switch(void);
void toggle_execute_switch(void);
void execute_instructions(void);
void set_accumulator(void);
void get_program_input(void);

/* Program variables */
int address_counter = 0; /* Address counter */
int accumulator = 0; /* Accumulator contents */
int input_temp = 0; /* Temporary storage for user input */
int mailbox[MAILBOXES]; /* "Mailboxes" - memory storage slots */

/* Program structures */
struct individual_led accumulator_led[12];
struct individual_led awaiting_input;
struct led_switch address_switch[8];
struct led_switch contents_switch[12];
struct led_switch power_switch;
struct led_switch clear_memory;
struct led_switch execute_switch;
struct led_switch user_input;

/* Program bitmaps */
BITMAP *front_plate = NULL;
BITMAP *led_on = NULL;
BITMAP *led_off = NULL;
BITMAP *switch_on = NULL;
BITMAP *switch_off = NULL;

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

    while (!key[KEY_ESC]) {
	if (keypressed()) {
	    get_input();
	}
    }

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

/* setup_structs: Assigns values to all of the appropriate structures in the
   program */
void setup_structs(void)
{
    int i;

    /* Start with the accumulator LEDs and contents LED/switch combos -
       12 of each */
    for (i = 0; i < 12; i++) {
	/* The accumulator LEDs start 50 pixels in, and are spaced at 30 pixel
	   increments. */
	accumulator_led[i].light_x = 50 + (i * 30);
	/* The accumulator LEDs are placed 40 pixels below the top edge of the
	   computer box. */
	accumulator_led[i].light_y = BUFFER_HEIGHT + 40;
	/* All of the LEDs are turned off by default. */
	accumulator_led[i].activity_status = 0;

	/* The contents LEDs and switches start 50 pixels in, and are spaced
	   at 30 pixel increments. */
	contents_switch[i].light_x = contents_switch[i].switch_x =
	    50 + (i * 30);
	/* The contents LEDs are placed 160 pixels below the top edge of the
	   computer box. */
	contents_switch[i].light_y = BUFFER_HEIGHT + 160;
	/* The contents switches are placed 35 pixels below the LEDs. */
	contents_switch[i].switch_y = contents_switch[i].light_y + 35;
	contents_switch[i].activity_status = 0;
	/* The default "last access time" is -1; this states that the switches
	   have not yet been activated. */
	contents_switch[i].last_access_time = -1;
    }

    /* Now we initialise the memory address switches. */
    for (i = 0; i < 8; i++) {
	/* The address LEDs and switches start 170 pixels in, and are spaced
	   at 30 pixel increments. */
	address_switch[i].light_x = address_switch[i].switch_x
	    = 170 + (i * 30);
	/* The address LEDs are placed 80 pixels below the top edge of the
	   computer box. */
	address_switch[i].light_y = BUFFER_HEIGHT + 80;
	/* The address switches are placed 35 pixels below the LEDs. */
	address_switch[i].switch_y = address_switch[i].light_y + 35;
	address_switch[i].activity_status = 0;
	address_switch[i].last_access_time = -1;
    }

    /* Next, we do the "awaiting input" LED. */
    /* The LED is placed 450 pixels in. */
    awaiting_input.light_x = 450;
    /* The LED is placed 40 pixels below the top edge of the computer box. */
    awaiting_input.light_y = BUFFER_HEIGHT + 40;
    awaiting_input.activity_status = 0;

    /* Then, we want to do the user input switch. */
    /* The LED and switch are placed on the same x coordinate as the "awaiting
       input" light. */
    user_input.light_x = user_input.switch_x = awaiting_input.light_x;
    /* The LED is placed 110 pixels below the top edge of the computer box. */
    user_input.light_y = BUFFER_HEIGHT + 110;
    /* The switch is placed 35 pixels below the LED. */
    user_input.switch_y = user_input.light_y + 35;
    /* We set the activity status of this just in case, even though we don't
       need it. */
    user_input.activity_status = 0;
    user_input.last_access_time = -1;

    /* Finally, we do the power switch, the "clear memory" switch and the
       "execute instructions" switch. */
    /* All of these LEDs and switches have the same x coordinate; 500 pixels
       in. */
    power_switch.light_x = clear_memory.light_x = execute_switch.light_x
	= power_switch.switch_x = clear_memory.switch_x 
	= execute_switch.switch_x = 500;
    /* The power switch is placed individually; the other y coordinates are
       based on pre-existing coordinates. */
    power_switch.light_y = BUFFER_HEIGHT + 20;
    power_switch.switch_y = power_switch.light_y + 30;
    clear_memory.light_y = address_switch[0].light_y + 10;
    clear_memory.switch_y = address_switch[0].switch_y;
    execute_switch.light_y = contents_switch[0].light_y;
    execute_switch.switch_y = contents_switch[0].switch_y;

    power_switch.activity_status = clear_memory.activity_status =
	execute_switch.activity_status = 0;
    power_switch.last_access_time = clear_memory.last_access_time =
	execute_switch.last_access_time = -1;
}

/* setup_screen: Sets graphics mode, then calls the setup_components() function
   to draw the screen. */
void setup_screen(void)
{
    /* Change video mode to 640x480 windowed */
    int ret = set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 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 */

    /* Load bitmaps into memory */
    if (front_plate == NULL)
	front_plate = load_bitmap("front_plate.pcx", NULL);
    if (led_on == NULL)
	led_on = load_bitmap("led_on.pcx", NULL);
    if (led_off == NULL)
	led_off = load_bitmap("led_off.pcx", NULL);
    if (switch_on == NULL)
	switch_on = load_bitmap("switch_on.pcx", NULL);
    if (switch_off == NULL)
	switch_off = load_bitmap("switch_off.pcx", NULL);

    setup_front_plate();
    setup_accumulator();
    setup_memory_addresses();
    setup_memory_contents();
    setup_power_switch();
    setup_execute_switch();
    setup_clear_switch();
    setup_input_light();
    setup_uinput_switch();
}

/* setup_front_plate: Blits the front plate bitmap to the screen. */
void setup_front_plate(void)
{
    blit(front_plate, screen, 0, 0, 0, 115, SCREEN_W, 250);
}

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

    /* Blit the unlit LEDs to the screen in sequence. */
    for (i = 0; i < 12; i++)
	blit(led_off, screen, 0, 0, accumulator_led[i].light_x,
	     accumulator_led[i].light_y, LED_WIDTH, LED_HEIGHT);

    /* Print a label 5 pixels below the row of LEDs; 25 pixels below their
       top point. */
    textout_ex(screen, font, "ACCUMULATOR", 200,
	       accumulator_led[0].light_y + 25, 15, -1);
}

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

    /* Blit the unlit LEDs and switches to the screen in sequence. */
    for (i = 0; i < 8; i++) {
	blit(led_off, screen, 0, 0, address_switch[i].light_x,
	     address_switch[i].light_y, LED_WIDTH, LED_HEIGHT);
	blit(switch_off, screen, 0, 0, address_switch[i].switch_x,
	     address_switch[i].switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
    }

    /* Print a label 5 pixels below the row of LEDs; 25 pixels below their
       top point. */
    textout_ex(screen, font, "MEMORY ADDRESS", 200, 
	       address_switch[0].light_y + 25, 15, -1);
}

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

    /* Blit the unlit LEDs and switches to the screen in sequence. */
    for (i = 0; i < 12; i++) {
	blit(led_off, screen, 0, 0, contents_switch[i].light_x,
	     contents_switch[i].light_y, LED_WIDTH, LED_HEIGHT);
	blit(switch_off, screen, 0, 0, contents_switch[i].switch_x,
	     contents_switch[i].switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
    }

    /* Print a label 5 pixels below the row of LEDs; 25 pixels below their
       top point. */
    textout_ex(screen, font, "ADDRESS CONTENTS", 200, 
	       contents_switch[0].light_y + 25, 15, -1);
}

/* setup_power_switch: Draws the power toggle light and switch in their default
   "off" state */
void setup_power_switch(void)
{
    /* Blit the unlit LED and switch to the screen. */
    blit(led_off, screen, 0, 0, power_switch.light_x, power_switch.light_y,
	 LED_WIDTH, LED_HEIGHT);
    blit(switch_off, screen, 0, 0, power_switch.switch_x, power_switch.switch_y,
	 SWITCH_WIDTH, SWITCH_HEIGHT);

    /* Print a label 10 pixels beside the LED; 30 pixels to its leftmost point
       and 5 pixels below its top point. */
    textout_ex(screen, font, "POWER", power_switch.light_x + 30,
	       power_switch.light_y + 5, 15, -1);
}

/* setup_clear_switch: Draws the clear memory light and switch in their default
   "off" state */
void setup_clear_switch(void)
{
    /* Blit the unlit LED and switch to the screen. */
    blit(led_off, screen, 0, 0, clear_memory.light_x,
	 clear_memory.light_y, LED_WIDTH, LED_HEIGHT);
    blit(switch_off, screen, 0, 0, clear_memory.switch_x,
	 clear_memory.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);

    /* Print a label 10 pixels beside the LED; 30 pixels to its leftmost point
       and 5 pixels below its top point. */
    textout_ex(screen, font, "CLEAR", clear_memory.light_x + 30,
	       clear_memory.light_y + 5, 15, -1);
    textout_ex(screen, font, "MEMORY", clear_memory.light_x + 30,
	       clear_memory.light_y + 15, 15, -1);
}

/* setup_execute_switch: Draws the execute instructions light and switch in
   their default "off" state */
void setup_execute_switch(void)
{
    /* Blit the unlit LED and switch to the screen. */
    blit(led_off, screen, 0, 0, execute_switch.light_x,
	 execute_switch.light_y, LED_WIDTH, LED_HEIGHT);
    blit(switch_off, screen, 0, 0, execute_switch.switch_x,
	 execute_switch.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);

    /* Print a label 10 pixels beside the LED; 30 pixels to its leftmost point
       and 5 pixels below its top point. */
    textout_ex(screen, font, "EXECUTE", execute_switch.light_x + 30,
	       execute_switch.light_y + 5, 15, -1);
    textout_ex(screen, font, "INSTRUCTIONS", execute_switch.light_x + 30,
	       execute_switch.light_y + 15, 15, -1);
}

/* setup_input_light: Draws the input light in its default "off" state */
void setup_input_light(void)
{
    /* Blit the unlit LED to the screen. */
    blit(led_off, screen, 0, 0, awaiting_input.light_x,
	 awaiting_input.light_y, LED_WIDTH, LED_HEIGHT);

    /* Print a label 10 pixels below the LED; 30 pixels below its top point
       and 20 pixels left of its leftmost point. */
    textout_ex(screen, font, "AWAITING", awaiting_input.light_x - 20,
	       awaiting_input.light_y + 30, 15, -1);
    textout_ex(screen, font, "INPUT", awaiting_input.light_x - 20,
	       awaiting_input.light_y + 40, 15, -1);
}

/* setup_input_light: Draws the input light in its default "off" state */
void setup_uinput_switch(void)
{
    /* Blit the unlit LED and switch to the screen. */
    blit(led_off, screen, 0, 0, user_input.light_x,
	 user_input.light_y, LED_WIDTH, LED_HEIGHT);
    blit(switch_off, screen, 0, 0, user_input.switch_x,
	 user_input.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);

    /* Print a label 10 pixels below the switch; 45 pixels below its top point
       and 20 pixels left of its leftmost point. */
    textout_ex(screen, font, "USER", user_input.switch_x,
	       user_input.switch_y + 45, 15, -1);
    textout_ex(screen, font, "INPUT", user_input.switch_x,
	       user_input.switch_y + 55, 15, -1);
}

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

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

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

/* get_input: receives and resolves user input from the keyboard */
void get_input(void)
{
    if (key[KEY_P])
	toggle_power();

    if (power_switch.activity_status == 1) {
	/* Toggle address switches */
	if (key[KEY_Q])
	    toggle_address_switch(0);

	if (key[KEY_W])
	    toggle_address_switch(1);

	if (key[KEY_E])
	    toggle_address_switch(2);

	if (key[KEY_R])
	    toggle_address_switch(3);

	if (key[KEY_T])
	    toggle_address_switch(4);

	if (key[KEY_Y])
	    toggle_address_switch(5);

	if (key[KEY_U])
	    toggle_address_switch(6);

	if (key[KEY_I])
	    toggle_address_switch(7);

	/* Toggle contents switches */
	if (key[KEY_A])
	    toggle_contents_switch(0, 't');

	if (key[KEY_S])
	    toggle_contents_switch(1, 't');

	if (key[KEY_D])
	    toggle_contents_switch(2, 't');

	if (key[KEY_F])
	    toggle_contents_switch(3, 't');

	if (key[KEY_G])
	    toggle_contents_switch(4, 't');

	if (key[KEY_H])
	    toggle_contents_switch(5, 't');

	if (key[KEY_J])
	    toggle_contents_switch(6, 't');

	if (key[KEY_K])
	    toggle_contents_switch(7, 't');

	if (key[KEY_Z])
	    toggle_contents_switch(8, 't');

	if (key[KEY_X])
	    toggle_contents_switch(9, 't');

	if (key[KEY_C])
	    toggle_contents_switch(10, 't');

	if (key[KEY_V])
	    toggle_contents_switch(11, 't');

	/* Toggle clear memory switch */
	if (key[KEY_DEL])
	    toggle_clear_switch();

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

/* toggle_power: toggles power from on to off and vice versa; flips the switch
   and light status appropriately. */
void toggle_power(void)
{
    int i;

    /* Check if the switch was accessed in the last 300 milliseconds */
    if (power_switch.last_access_time < 0 || 	(clock() / (double) CLOCKS_PER_MS) - 	power_switch.last_access_time >= 300) {
	/* Set the new access time */
	power_switch.last_access_time = clock() / (double) CLOCKS_PER_MS;

	if (power_switch.activity_status == 0) {
	    /* Turn on power, change power switch activity status */
	    power_switch.activity_status = 1;
	    /* Turn on light, flip switch up */
	    blit(led_on, screen, 0, 0, power_switch.light_x,
		 power_switch.light_y, LED_WIDTH, LED_HEIGHT);
	    blit(switch_on, screen, 0, 0, power_switch.switch_x,
		 power_switch.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
	} else {
	    /* Turn off power, change power switch activity status */
	    power_switch.activity_status = 0;
	    /* Clear memory, reset screen, set address counter to 0 */
	    initialise_memory();
	    setup_components();
	    address_counter = 0;
	    /* Set all of the contents and address switch activity statuses
	       back to 0 */
	    for (i = 0; i < 8; i++)
		address_switch[i].activity_status = 0;
	    for (i = 0; i < 12; i++)
		contents_switch[i].activity_status = 0;
	}
    }
}

/* 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)
{
    /* Check if the switch was accessed in the last 300 milliseconds */
    if (address_switch[toggle].last_access_time < 0 || 	(clock() / (double) CLOCKS_PER_MS) - 	address_switch[toggle].last_access_time >= 300) {
	/* Set the new access time */
	address_switch[toggle].last_access_time =
	    clock() / (double) CLOCKS_PER_MS;

	if (address_switch[toggle].activity_status == 0) {
	    /* Change address switch activity status */
	    address_switch[toggle].activity_status = 1;
	    /* Turn on light, flip switch up */
	    blit(led_on, screen, 0, 0, address_switch[toggle].light_x,
		 address_switch[toggle].light_y, LED_WIDTH, LED_HEIGHT);
	    blit(switch_on, screen, 0, 0, address_switch[toggle].switch_x,
		 address_switch[toggle].switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
	} else {
	    /* Change address switch activity status */
	    address_switch[toggle].activity_status = 0;
	    /* Turn off light, flip switch down */
	    blit(led_off, screen, 0, 0, address_switch[toggle].light_x,
		 address_switch[toggle].light_y, LED_WIDTH, LED_HEIGHT);
	    blit(switch_off, screen, 0, 0, address_switch[toggle].switch_x,
		 address_switch[toggle].switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
	}

	/* Flip the bit of the program counter corresponding to the big-endian
	   position of the address switch */
	address_counter ^= 1 << (7 - toggle);
    }

    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;

    for (i = 0; i < 12; i++) {
	if (mailbox[address_counter] & (1 << (11 - i))) {
	    /* Change activity status appropriately */
	    contents_switch[i].activity_status = 1;
	    /* Turn on light, flip switch up */
	    blit(led_on, screen, 0, 0, contents_switch[i].light_x,
		 contents_switch[i].light_y, LED_WIDTH, LED_HEIGHT);
	    blit(switch_on, screen, 0, 0, contents_switch[i].switch_x,
		 contents_switch[i].switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
	} else {
	    /* Change activity status appropriately */
	    contents_switch[i].activity_status = 0;
	    /* Turn off light, flip switch down */
	    blit(led_off, screen, 0, 0, contents_switch[i].light_x,
		 contents_switch[i].light_y, LED_WIDTH, LED_HEIGHT);
	    blit(switch_off, screen, 0, 0, contents_switch[i].switch_x,
		 contents_switch[i].switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
	}
    }
}

/* 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, char mode)
{
    /* Check if the switch was accessed in the last 300 milliseconds */
    if (contents_switch[toggle].last_access_time < 0 || 	(clock() / (double) CLOCKS_PER_MS) - 	contents_switch[toggle].last_access_time >= 300) {
	/* Set the new access time */
	contents_switch[toggle].last_access_time =
	    clock() / (double) CLOCKS_PER_MS;

	if (contents_switch[toggle].activity_status == 0) {
	    /* Change activity status appropriately */
	    contents_switch[toggle].activity_status = 1;
	    /* Turn on light, flip switch up */
	    blit(led_on, screen, 0, 0, contents_switch[toggle].light_x,
		 contents_switch[toggle].light_y, LED_WIDTH, LED_HEIGHT);
	    blit(switch_on, screen, 0, 0, contents_switch[toggle].switch_x,
		 contents_switch[toggle].switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
	} else {
	    /* Change activity status appropriately */
	    contents_switch[toggle].activity_status = 0;
	    /* Turn off light, flip switch down */
	    blit(led_off, screen, 0, 0, contents_switch[toggle].light_x,
		 contents_switch[toggle].light_y, LED_WIDTH, LED_HEIGHT);
	    blit(switch_off, screen, 0, 0, contents_switch[toggle].switch_x,
		 contents_switch[toggle].switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
	}

	/* 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[address_counter] ^= 1 << (11 - toggle);
	if (mode == 'a')
	    input_temp ^= 1 << (11 - toggle);
    }
}

/* 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 up. Pause for effect */
    blit(led_on, screen, 0, 0, clear_memory.light_x,
	 clear_memory.light_y, LED_WIDTH, LED_HEIGHT);
    blit(switch_on, screen, 0, 0, clear_memory.switch_x,
	 clear_memory.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
    rest(150);

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

    /* Change positions of address contents switches */
    set_contents();

    /* Turn off light, flip switch up */
    blit(led_off, screen, 0, 0, clear_memory.light_x,
	 clear_memory.light_y, LED_WIDTH, LED_HEIGHT);
    blit(switch_off, screen, 0, 0, clear_memory.switch_x,
	 clear_memory.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
}

/* 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)
{
    /* Check if the switch was accessed in the last 300 milliseconds */
    if (execute_switch.last_access_time < 0 || 	(clock() / (double) CLOCKS_PER_MS) - 	execute_switch.last_access_time >= 300) {
	/* Set the new access time */
	execute_switch.last_access_time =
	    clock() / (double) CLOCKS_PER_MS;

	/* Turn on light, flip switch down. Pause for effect */
	blit(led_on, screen, 0, 0, execute_switch.light_x,
	 execute_switch.light_y, LED_WIDTH, LED_HEIGHT);
	blit(switch_on, screen, 0, 0, execute_switch.switch_x,
	 execute_switch.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
	rest(200);

        execute_instructions();

	/* Turn off light, flip switch up. */
	blit(led_off, screen, 0, 0, execute_switch.light_x,
	 execute_switch.light_y, LED_WIDTH, LED_HEIGHT);
	blit(switch_off, screen, 0, 0, execute_switch.switch_x,
	 execute_switch.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
    }
}

/* 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 pc = 0;
    int i;
    FILE *ofp = fopen("lmc_output", "a");

    accumulator = 0;
    do {
	opcode = mailbox[pc] / (OP_SPLIT + 1);
	operand = mailbox[pc] & OP_SPLIT;
	/* Increment the program counter in advance; if it's a jump instruction,
	 the PC will be set manually. */
	++pc;

	switch(opcode) {
	case 0x1:
	    accumulator += mailbox[operand];
	    break;
	case 0x2:
	    accumulator -= mailbox[operand];
	    break;
	case 0x3:
	    mailbox[operand] = accumulator;
	    break;
	case 0x4:
	    break; /* Treat as NOP */
	case 0x5:
	    accumulator = mailbox[operand];
	    break;
	case 0x6:
	    pc = operand;
	    break;
	case 0x7:
	    /* If accumulator is zero, branch; otherwise, progress to next
	       instruction - treat as NOP. */
	    if (accumulator == 0)
		pc = operand;
	    break;
	case 0x8:
	    /* If accumulator is greater than or equal to zero, branch;
	       otherwise, progress to next instruction - treat as NOP. */
	    if (accumulator >= 0)
		pc = operand;
	    break;
	case 0x9:
	    if (operand == 0x1) {
		get_program_input();
		accumulator = input_temp;
	    } else if (operand == 0x2) {
		fprintf(ofp, "%3x\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. */
	}

	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))
	    blit(led_on, screen, 0, 0, accumulator_led[i].light_x,
		 accumulator_led[i].light_y, LED_WIDTH, LED_HEIGHT);
	else
	    blit(led_off, screen, 0, 0, accumulator_led[i].light_x,
		 accumulator_led[i].light_y, LED_WIDTH, LED_HEIGHT);
    }
}

/* get_program_input: Allows entry of data into the temporary input storage
   buffer using the memory contents switches. */
void get_program_input(void)
{
    int i;

    /* Clear temporary input storage buffer */
    input_temp = 0;

    /* Clear all contents switches */
    for (i = 0; i < 12; i++) {
	contents_switch[i].activity_status = 0;
	blit(led_off, screen, 0, 0, contents_switch[i].light_x,
	     contents_switch[i].light_y, LED_WIDTH, LED_HEIGHT);
	blit(switch_off, screen, 0, 0, contents_switch[i].switch_x,
	     contents_switch[i].switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
    }

    /* Turn on "awaiting input" LED */
    blit(led_on, screen, 0, 0, awaiting_input.light_x, awaiting_input.light_y,
	 LED_WIDTH, LED_HEIGHT);

    /* Basically, keep taking contents entries while the Space key isn't pressed
       and the last access time period of 300 milliseconds hasn't been met. Yes,
       it's a very complex test. */
    for(;;) {
	/* Toggle contents switches */
	if (key[KEY_A])
	    toggle_contents_switch(0, 'a');

	if (key[KEY_S])
	    toggle_contents_switch(1, 'a');

	if (key[KEY_D])
	    toggle_contents_switch(2, 'a');

	if (key[KEY_F])
	    toggle_contents_switch(3, 'a');

	if (key[KEY_G])
	    toggle_contents_switch(4, 'a');

	if (key[KEY_H])
	    toggle_contents_switch(5, 'a');

	if (key[KEY_J])
	    toggle_contents_switch(6, 'a');

	if (key[KEY_K])
	    toggle_contents_switch(7, 'a');

	if (key[KEY_Z])
	    toggle_contents_switch(8, 'a');

	if (key[KEY_X])
	    toggle_contents_switch(9, 'a');

	if (key[KEY_C])
	    toggle_contents_switch(10, 'a');

	if (key[KEY_V])
	    toggle_contents_switch(11, 'a');

	/* I recognise that wrapping this test up in an infinite loop is bad
	   practice, but I couldn't get the last access time to work in the
	   main test of the while loop, and the key bounce issue from last
	   time was plaguing this part of the program again. */
	if (key[KEY_SPACE] && (user_input.last_access_time < 0 || 	(clock() / (double) CLOCKS_PER_MS) - 			       user_input.last_access_time >= 300))
	    break;

	/* We also need a way to escape from this part of the program
	   quickly, in order to allow exiting from all states in the program. */
	if (key[KEY_ESC])
	    return;
    }

    /* Set the last access time for the user input switch */
    user_input.last_access_time = clock() / (double) CLOCKS_PER_MS;

    /* Turn on light, flip switch down. Pause for effect */
    blit(led_on, screen, 0, 0, user_input.light_x,
	 user_input.light_y, LED_WIDTH, LED_HEIGHT);
    blit(switch_on, screen, 0, 0, user_input.switch_x,
	 user_input.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);
    rest(200);

    /* Turn off light, flip switch up. */
    blit(led_off, screen, 0, 0, user_input.light_x,
	 user_input.light_y, LED_WIDTH, LED_HEIGHT);
    blit(switch_off, screen, 0, 0, user_input.switch_x,
	 user_input.switch_y, SWITCH_WIDTH, SWITCH_HEIGHT);

    /* Turn off the "awaiting input" LED */
    blit(led_off, screen, 0, 0, awaiting_input.light_x, awaiting_input.light_y,
	 LED_WIDTH, LED_HEIGHT);

    /* Reset the contents switches */
    set_contents();
}