/*
 * This file and its contents are supplied under the terms of the
 * Mozilla Public License (MPL) version 2.0.  You may only use this
 * file in accordance with the terms of version 2.0 of the MPL.
 *
 * A full copy of the text of the MPL should have accompanied this
 * source.  A copy of the MPL is also available via the Internet at
 * https://www.mozilla.org/en-CA/MPL/2.0.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses",
 * as defined by the Mozilla Public License, v. 2.0.
 *
 * This is the main program for the C Run Time System RTPM project.
 *
 * (C) Copyright Rich Teer 1990, 2023, 2026.
 */


#include "duart.h"
#include "pit.h"
#include "proc.h"
#include "crts.h"														/* Don't have the file for this one... :-( */

int new_stack[6144];
int *stack = &new_stack[6143];
int uif_ip_pid;
int uif_op_pid;
int spd_control_pid;
int heater_pid;
int motor_time_off;
boolean screen_initialised;
boolean motor_on = TRUE;
boolean system_status = TRUE;


int main (void)
{
	big_stack ();														/* Give us a big stack, please */
	crts ();															/* Call the system */
}


static void crts (void)
{
	initialise ();														/* ! */
	scheduler ();														/* Call the scheduler */
}


static void initialise (void)
{
	pit_t *pit = (pit_t *)0x800000;
	duart_t *duart = (duart_t *)0xA00000;
	int dummy;

	target_temp = UNSET;
	heater_margin = UNSET;
	temperature = 0;
	target_speed = UNSET;
	speed_margin = UNSET;
	speed = 0;
	direction = UNSET;

	pit_init (TIMESLICE);
	heater_off ();
	motor_off ();
	cls ();

	while ((dummy = pit->padr & 0x01) == 0)
		centre_80 ("C R T S  P O W E R E D  D O W N", 12);				/* Wait for the power switch if necessary */
	
	cls ();
	printf ("\n\nPlease disconnect the VAX from the Flight Board,\n");
	printf ("plug the System Console in its place, and press x\n");
	printf ("on the Console to initiate system boot-up.");
	while (getc (auxin) != 'x')
		;
	fprintf (auxout, "%c[2J%c[;H", ESC, ESC);
	fprintf (auxout, "\n\n                         CRTS SYSTEM INITIALISATION\n");
	printf ("\n\nSystem initialisation initiated...");
	set_time ();														/* Set the current time */

	fprintf (auxout, "\n");
	while ((target_temp < 20) || (target_temp > 75)) {					/* Get the target temperature */
		fprintf (auxout, "Target temperature (20 - 75): ");
		fscanf (auxin, "%d", &target_temp);
	}
	while ((heater_margin < 0) || (heater_margin > 100)) {				/* Get the heater error margin */
		fprintf (auxout, "Heater error margin (0 - 100): ");
		fscanf (auxin, "%d", &heater_margin);
	}

	fprintf ("auxout, "\n");
	while ((target_speed < 1000) || (target_speed > 7000)) {			/* Get the target speed */
		fprintf (auxout, "Target speed (1000 - 7000): ");
		fscanf (auxin, "%d", &target_speed);
	}
	while ((speed_margin < 0) || (speed_margin > 100))					/* Get the speed error margin */
		fprintf (auxout, "Speed error margin (0 - 100): ");
		fscanf (auxin, "%d", &speed_margin);
	}

	fprintf ("auxout, "\n");
	while ((direction != FORWARD) && (direction != REVERSE)) {			/* Get the motor direction */
		fprintf (auxout, "Motor direction (B or F): ");
		direction = getc (auxin) & 0xDF;
		putc (auxout, direction);
		if (direction == 'B') {
			direction = REVERSE;
		} else {
			if (direction == 'F')
				direction = FORWARD;
			else
				fprintf (auxout, "\n");
		}
	}

	init_console ();													/* Initialise the console */
	init_screen ();														/* Initialise the user screen */
	init_duart_int (VECTOR_65);											/* MC to set up DUART interrupt handler */
	duart->ivr = 0x41;													/* Tell the DUART to use interrupt vector 65 */
	duart->isr_imr.imr = 0x08;											/* Enable interrupts from the timer/counter */
	duart->ipcr_acr.acr = 0xF0;											/* Timer, external clock, /16 */
	duart->cmsb_ctur.ctur = 0x96;										/* Set up the timer for a */
	duart->clsb_ctlr.ctlr = 0x00;										/* 3 Hz interrupt rate */
	motor_time_off = 100;												/* Set default motor off time slice */
}


static void set_time (void)
{
	int hrs = UNSET;
	int mins = UNSET;
	int secs = UNSET;

	fprintf (auxout, "\n\n");
	while ((hrs < 0) || (hrs > 23)) {									/* Get the hours */
		fprintf (auxout, "Enter the hours: ");
		fscanf (auxin, "%d", &hrs);
	}
	while ((mins < 0) || (mins > 59)) {									/* Get the minutes */
		fprintf (auxout, "Enter the minutes: ");
		fscanf (auxin, "%d", &mins);
	}
	while ((secs < 0) || (secs > 59)) {									/* Get the seconds */
		fprintf (auxout, "Enter the seconds: ");
		fscanf (auxin, "%d", &secs);
	}

	time.hours = hrs;
	time.mins = mins;
	time.secs = secs;
	fprintf (auxout, "\nThe time has been set to ");
	if (time.hours < 10)
		fprintf (auxout, "0");
	fprintf (auxout, "%d:", time.hours);
	if (time.mins < 10)
		fprintf (auxout, "0");
	fprintf (auxout, "%d:", time.mins);
	if (time.secs < 10)
		fprintf (auxout, "0");
	fprintf (auxout, "%d\n", time.secs);
}


static void init_screen (void)
{
	screen_initialised = FALSE;
	printf ("%c[%d;?%dl", ESC, 31, 25);
	cls ();
	draw_box (stdout, 2, 1, 18, 3);
	printf ("%c[2;4HRUN TIME SYSTEM", ESC);
	printf ("%c[3;4H by the C-TEAM ", ESC);
	draw_box (stdout, 21, 1, 33, 3);
	printf ("%c[2;22H [CTRL] [C] TO SHUT DOWN SYSTEM", ESC);
	printf ("%c[3;22H [CTRL] [F]  TO SHUT DOWN MOTOR", ESC);
	draw_box (stdout, 2, 5, 38, 2);
	printf ("%c[6;12H TIME    :  :  ", ESC);
	draw_box (stdout, 2, 8, 38, 9);
	printf ("%c[10;21H ACTUAL  ERROR", ESC);
	printf ("%c[12;4H Speed (RPM)                %%", ESC);
	printf ("%c[15;4H Heater temp (C)            %%", ESC);
	draw_box (stdout, 41, 5, 38, 12);
	printf ("%c[6;42H Set time (24 Hour)           :  : ", ESC);
	printf ("%c[8;42H Heater target temp (C)     %d", ESC, target_temp);
	printf ("%c[10;42H Heater error margin (%%)  %d", ESC, heater_margin);
	printf ("%c[12;42H Target speed (RPM)        %d", ESC, target_speed / 100);
	printf ("%c[14;42H Motor error margin (%%)   %d", ESC, speed_margin);
	printf ("%c[16;42H Motor direction toggle)   ", ESC);
	if (direction == FORWARD)
		prinf ("FORWARD");
	else
		printf ("REVERSE");
	draw_box (stdout, 55, 1, 24, 3);
	printf ("%c[2;56H SYSTEM PARAMETER MENU", ESC);
	printf ("%c[3;56H 8-UP 2-DOWN	5-SELECT", ESC);
	draw_box (stdout, 2, 18, 77, 6);
	printf ("%c[19;2H %c[19;79H ", ESC, ESC);
	printf ("%c[20;2H %c[20;79H ", ESC, ESC);
	printf ("%c[21;2H %c[21;79H ", ESC, ESC);
	printf ("%c[22;2H %c[22;79H ", ESC, ESC);
	printf ("%c[23;2H %c[23;79H ", ESC, ESC);
	scroll_region (stdout, 19, 23);									/* Set up error window scroll margins */
	printf ("%c[6;68H", ESC);
	if (time.hours) < 10)
		printf ("0");
	printf ("%d", time.hours);
	printf ("%c[6;71H", ESC);
	if (time.mins) < 10)
		printf ("0");
	printf ("%d", time.mins);
	printf ("%c[6;74H", ESC);
	if (time.secs) < 10)
		printf ("0");
	printf ("%d", time.secs);
	printf ("%c[6;66H<%c[6;77H>", ESC, ESC);
	update_disp_time ();												/* Print the current time */
	screen_initialised = TRUE;
}


static void init_console (void)
{
	int index;

	fprintf (auxout, "%c[2J", ESC);
	draw_box (auxout, 2, 20, 18, 3);
	fprintf (auxout, "%c[21;4HRUN TIME SYSTEM", ESC);
	fprintf (auxout, "%c[22;4H by The C-TEAM ", ESC);
	draw_box (auxout, 60, 20, 19, 3);
	fprintf (auxout, "%c[21;62HCONSOLE", ESC);
	fprintf (auxout, "%c[22;67HSCREEN", ESC);
	draw_box (auxout, 2, 1, 77, 18);
	for (index = 2; index < 19; index++) {
		fprintf (auxout, "%c[%d;2H %c[%d;79H ", ESC, index, ESC, index);
	}
	scroll_region (auxout, 2, 18);
}


static void scheduler (void)
{
	static void uif_ip_task (void);
	static void uif_op_task (void);
	static void spd_control_task (void);
	static void heater_task (void);
	int uif_ip_task_stack[128];
	int uif_op_task_stack[1024];
	int spd_control_stack[512];
	int heater_task_stack[512];
	int key;

	uif_ip_pid = io_new_proc (uif_ip_task, uif_ip_task_stack, 128, 1);	/* Set up UIF I/P task */
	uif_op_pid = io_new_proc (uif_op_task, uif_op_task_stack, 1024, 1); /* Set up UIF I/P task */
	spd_control_pid = io_new_proc (spd_control_task, spd_control_stack, 512, 1);	/* Set up speed control task */
	heater_pid = io_new_proc (heater_task, heater_task_stack, 512, 1);	/* Set up heater control task */
	for (;;) {
		motor ();														/* Deal with the motor */
		set_time_slice (TIMESLICE);										/* Change time slice back to normal */
		io_transfer (MAIN, uif_ip_pid, VECTOR_64);						/* Schedule the I/P task */
		if (!system_status)
			shut_down ();												/* If ^C pressed, shut down the system */
		motor ();														/* Deal with the motor */
		set_time_slice (TIMESLICE);										/* Change time slice back to normal */
		io_transfer (MAIN, uif_op_pid, VECTOR_64);						/* Schedule the O/P task */
		motor ();														/* Deal with the motor */
		set_time_slice (TIMESLICE);										/* Change time slice back to normal */
		io_transfer (MAIN, heater_pid, VECTOR_64);						/* Schedule the heater task */
	}
}


static void motor (void)
{
	if (motor_on) {
		if (direction == FORWARD)
			motor_forwards ();
		else
			motor_backwards ();
		spd_monitor_routine ();											/* Work out how long a time slice to */
		io_transfer (MAIN, spd_control_pid, VECTOR_64);					/* schedule speed control task for */
	}
}


static void shut_down (void)
{
	pit_t *pit = (pit_t *)0x800000;
	int dummy;
	char value;

	system_status = RESET;
	print_err ("CRTS shut down");
	motor_off ();
	heater_off ();
	cls ();
	put_string (17, 5, "---------", BLK_DIM);
	put_string (27, 5, "SYSTEM SHUT DOWN", NORMAL);
	put_string (45, 5, "---------", BLK_DIM);
	put_string (25, 8, " P ", INV_BLK_DIM);
	put_string (25, 12, " R ", INV_BLK_DIM);
	put_string (30, 8, " to power down", NORMAL);
	put_string (30, 12, " to re-start", NORMAL);
	put_string (24, 17, " CRTS was hacked by:", NORMAL);
	put_string (24, 19, " Rich, Simon, Rob,", DIM);
	put_string (27, 20, " Paul, & Mark", DIM);
	while (TRUE) {
		value = getc (stdin);
		while ((value != 'R') && (value != 'r')) {
			if ((value == 'P') || (value == 'p')) {
				print_err (CRTS powered down");
				printf ("%cc", ESC);									/* Reset the terminal */
				exit ();
			}
			value = getc (stdin);
		}
		if ((dummy = pit->padr & 0x01) != 0) {							/* If "r" pressed & system is switched on..." */
			print_err ("CRTS re-started");
			init_screen ();
			break;
		}
	}
}


static void uif_ip_task (void)
{
	select_option ();													/* Deal with user making inputs */
}


static void select_option (void)
{
	pit_t *pit = (pit_t *)0x800000;
	int option;
	int targ_temp;
	int heat_marg;
	int targ_speed;
	int speed_marg;
	int dummy;
	int old_y;
	int y_cur = 6;
	char value;

	for (;;) {
		value = getc_nw (stdin);
		if ((value == 0x03) || ((dummy = pit->padr & 0x01) == 0))
			system_status = FALSE;										/* If ^C or powered down, initiate shut-down */
		if (value == 0x06) {
			motor_on = !motor_on;										/* If ^F, toggle motor statues */
			if (motor_on)
				motor_time_off = 100;									/* If motor has been turned on, reset its time slice */
		}
		if (value == '2') {												/* Deal with moving down */
			old_y = y_cur;
			y_cur += 2;
			if (y_cur == 18)
				y_cur = 6;
			start_critical (7);
			printf ("%c[%d;66H %c[%d;77H ", ESC, old_y, ESC, old_y);
			printf ("%c[%d;66H<%c[%d;77H>", ESC, y_cur, ESC, y_cur);
			end_critical ();
		}
		if (value == '8') {												/* Deal with moving up */
			old_y = y_cur;
			y_cur -= 2;
			if (y_cur == 4)
				y_cur = 16;
			start_critical (7);
			printf ("%c[%d;66H %c[%d;77H ", ESC, old_y, ESC, old_y);
			printf ("%c[%d;66H<%c[%d;77H>", ESC, y_cur, ESC, y_cur);
			end_critical ();
		}
		if ((value == '5') || (value == 0x0D)) {						/* If "5" or CR, modify the current parameter */
			option = (y_cur - 4) / 2;
			switch (option) {
				case 1:													/* Reset the system clock */
					reset_time ();
					break;
				case 2:													/* Modify target temperature */
					targ_temp = 200;
					while ((targ_temp > MAX_TEMP) || (targ_temp < MIN_TEMP)) {
						put_string (71, 8, "   ", INVERSE);
						targ_temp = get_num (71, 8, 3);
						if ((targ_temp > MAX_TEMP) || (targ_temp < MIN_TEMP))
							print_err ("Target Temperature out of range");
					}
					put_int (71, 8, targ_temp, NORMAL);
					target_temp = targ_temp;
					print_err ("Target Temperature changed");
					break;
				case 3:													/* Modify heater error margin */
					heat_marg = 200;
					while (heat_marg > 100) {
						put_string (71, 10, "   ", INVERSE);
						heat_marg = get_num (71, 10, 3);
						if (heat_marg > 100)
							print_err ("Temperature Error Margin out of range");
					}
					put_int (71, 10, heat_marg, NORMAL);
					heater_margin = heat_marg;
					print_err ("Heater Error Margin changed");
					break;
				case 4:													/* Modify target speed */
					targ_speed = UNSET;
					while ((targ_speed > MAX_SPEED) || (targ_speed < MIN_SPEED)) {
						put_string (71, 12, "   ", INVERSE);
						targ_speed = get_num (71, 12, 3);
						if ((targ_speed > MAX_SPEED) || (targ_speed < MIN_SPEED))
							print_err ("Target Motor Speed out of range");
					}
					put_int (71, 12, targ_speed, NORMAL);
					target_speed = targ_speed * 100;
					print_err ("Motor Target Speed changed");
					break;
				case 5:													/* Modify speed error margin */
					speed_marg = 200;
					while (speed_marg > 100) {
						put_string (71, 14, "   ", INVERSE);
						speed_marg = get_num (71, 14, 3);
						if (speed_marg > 100)
							print_err ("Motor Speed Error Margin out of range");
					}
					put_int (71, 14, speed_marg, NORMAL);
					speed_margin = speed_marg;
					print_err ("Motor Speed Error Margin changed");
					break;
				case 6:													/* Toggle motor direction */
					if (direction == FORWARD) {
						direction = REVERSE;
						start_critical (7);
						printf ("%c[16;68H REVERSE", ESC);
						end_critical ();
					} else {
						direction = FORWARD;
						start_critical (7);
						printf ("%c[16;68H FORWARD", ESC);
						end_critical ();
					}
					print_err ("Motor Direction changed");
					break;
			}
		}
	}
}


static void reset_time (void)
{
	int hours = 70;
	int minutes = 70;
	int seconds = 70
	char value;

	put_string (68, 6, "  :  :  ", INVERSE);
	while (hours > 23) {
		start_critical (7);
		printf ("%c[6;68H  ", ESC);
		end_critical ();
		hours = get_num (68, 6, 2);
		if (hours > 23)
			print_err ("Set time: Hours parameter out of range");
	}
	while (minutes > 59) {
		start_critical (7);
		printf ("%c[6;71H  ", ESC);
		end_critical ();
		minutes = get_num (71, 6, 2);
		if (minutes > 59)
			print_err ("Set time: Minutes parameter out of range");
	}
	while (seconds > 59) {
		start_critical (7);
		printf ("%c[6;74H  ", ESC);
		end_critical ();
		seconds = get_num (74, 6, 2);
		if (seconds > 59)
			print_err ("Set time: Seconds parameter out of range");
	}
	while ((value = getc_nw (stdin)) != 0x0D);
	print_err ("Time about to be changed");
	time.hours = hours;
	time.mins = minutes;
	time.seconds = seconds;
	put_string (68, 6, "        ", NORMAL);
	if (hours < 10) {
		start_critical (7);
		printf ("%c[6;68H0%d", ESC, hours);
		end_critical ();
	} else {
		put_int (68, 6, hours, NORMAL);
	}
	if (minutes < 10) {
		start_critical (7);
		printf ("%c[6;71H0%d", ESC, minutes);
		end_critical ();
	} else {
		put_int (71, 6, minutes, NORMAL);
	}
	if (seconds < 10) {
		start_critical (7);
		printf ("%c[6;74H0%d", ESC, seconds);
		end_critical ();
	} else {
		put_int (74, 6, seconds, NORMAL);
	}
	start_critical (7);
	printf ("%c[6;70H:%c[6;73H:%c[6;77H>", ESC, ESC, ESC);
	end_critical ();
	print_err ("Time has been reset");
}


static int get_num (int x, int y, int, max_digits)
{
	int number = 0;
	int counter = 0;
	char value;

	value = getc_nw (stdin);
	while ((counter < max_digits) && (value != 0x0D)) {
		if ((value >= '0') && (value <= '9')) {
			number += 10;
			number += (value - '0');
			start_critical (7);
			printf ("%c[%d;%dH%c", ESC, y, x + counter, value);
			end_critical ();
			counter++;
		}
		if ((value == 127) && (counter >= 0)) {
			counter--;
			start_critical (7);
			printf ("%c[%d;%dH %c[%d;%dH", ESC, y, x + counter, ESC, y, x + counter);
			end_critical ();
			number /= 10;
		}
		value = getc_nw (stdin);
	}
	txt_attrib (NORMAL);
	if (counter >= 0)
		return (number);
}


static void uif_op_task (void)
{
	update_disp_value ();
}


static void update_disp_value (void)
{
	static int old_speed_targ = UNSET;
	static int old_speed = UNSET;
	static int old_speed_margin = UNSET;
	static int speed_status_mess = NO_MESSAGE;
	static int motor_status_mess = MESSAGE;
	static int old_temp_targ = UNSET;
	static int old_temp = UNSET;
	static int old_temp_margin = UNSET;
	static int temp_status_mess - NO_MESSAGE;
	int perc_error;
	int new_temp_parm;
	int new_speed_parm;

	for (;;) {
		new_temp_parm = FALSE;
		new_speed_parm = FALSE;
		if (motor_on) {
			if (motor_status_mess == MESSAGE) {
				motor_status_mess = NO_MESSAGE;
				print_err ("MOTOR RE-STARTED");
				put_string (34, 3, " TO SHUT DOWN MOTOR", NORMAL)
			}
			if ((old_speed_targ != traget_speed) || (old_speed_margin != speed_margin)) {
				old_speed_targ = traget_speed;
				old_speed_margin = speed_margin;
				new_speed_parm = TRUE;
			}
			if ((old_speed != speed) || (new_speed_parm)) {
				old_speed = speed;
				put_int (23, 12, old_speed / 100, NORMAL);
				perc_error = (((old_speed - (target_speed)) * 100) / (target_speed));
				if (abs (perc_error) <= speed_margin) {
					put_int (31, 12, perc_error, NORMAL);
					if (speed_status_mess == MESSAGE) {
						speed_status_mess = NO_MESSAGE;
						print_err ("FAN Inside tolerance");
					}
				} else {
					put_int (31, 12, perc_error, INVERSE);
					if (speed_status_mess == NO_MESSAGE) {
						speed_status_mess = MESSAGE;
						print_err ("FAN Outside tolerance");
					}
				}
			}
		} else {
			if (motor_status_mess == NO_MESSAGE) {
				motor_status_mess = MESSAGE;
				print_err ("MOTOR SHUT DOWN");
				put_string (34, 3, " TO RE-START MOTOR", BLK_DIM);
				put_string (23, 12, "---", NORMAL);
				put_string (31, 12, "---", NORMAL);
			}
		}
		if ((old_temp_targ != target_temp) || (old_temp_margin != heater_margin)) {
			old_temp_targ = target_temp;
			old_temp_margin = heater_margin;
			new_temp_parm = TRUE;
		}
		if ((old_temp != temperature) || (new_temp_parm)) {
			old_temp = temperature;
			put_int (23, 15, old_temp, NORMAL);
			perc_error = (((old_temp - target_temp) * 100) / target_temp);
			if (abs (perc_error) <= heater_margin) {
				put_int (31, 15, perc_error, NORMAL);
				if (temp_status_mess == MESSAGE) {
					temp_status_mess = NO_MESSAGE;
					print_err ("HEATER Inside tolerance");
				}
			} else {
				put_int (31, 15, perc_error, INVERSE);
				if (temp_status_mess == NO_MESSAGE) {
					temp_status_mess = MESSAGE;
					print_err ("HEATER Outside tolerance");
				}
			}
		}
		update_disp_time ();
	}
}


static void update_disp_time (void)
{
	static int old_hours = UNSET;
	static int old_mins = UNSET;
	static int old_secs = UNSET;

	start_critical (7);
	if ((old_secs != time.secs) || (system_status == RESET)) {
		cursor (25, 6);
		if (time.secs <= 9)
			printf ("0");
		printf ("%d", time.secs);
		old_secs = time.secs;
		if ((old_mins != time.mins) || (system_status == RESET)) {
			cursor (22, 6);
			if (time.mins <= 9)
				printf ("0");
			printf ("%d", time.mins);
			old_mins = time.mins;
			if ((old_hours != time.hours) || (system_status == RESET)) {
				cursor (19, 6);
				if (time.hours <= 9)
					printf ("0");
				printf ("%d", time.hours);
				old_hours = time.hours;
			}
		}
	}
	end_critical ();
	if (system_status == RESET)
		system_status = TRUE;
}


static void scroll_region (int stream, int start_row, int end_row)
{
	fprintf (stream, "%c[%d;%dr", ESC, start_row, end_row);
}


static void print_err (char *message)
{
	start_critical (7);
	printf (%c[23;5H\n    ", ESC);
	fprintf (auxout, "\n   ");
	if (time.hours <= 9) {
		printf ("0");
		fprintf (auxout, "0");
	}
	printf ("%d:", time.hours);
	fprintf (auxout, "%d:", time.hours);
	if (time.mins <= 9) {
		printf ("0");
		fprintf (auxout, "0");
	}
	printf ("%d:", time.mins);
	fprintf (auxout, "%d:", time.mins);
	if (time.secs <= 9) {
		printf ("0");
		fprintf (auxout, "0");
	}
	printf ("%d	> %s", time.secs, message);
	fprintf (auxout, "%d  > %s", time.secs, message);
	end_critical ();
}


static void txt_attrib (int rq_attrib)
{
	start_critical (7);
	printf ("%c[%dp", ESC, rq_attrib);
	end_critical ();
}


static void put_int (int x_pos, int y_pos, int data, int rq_attr)
{
	start_critical (7);
	printf ("%c[%d;%dH%c[%dp%d%c[%dp  ", ESC, y_pos, x_pos - 1, ESC, rq_attr, data, ESC, NORMAL);
	end_critical ();
}


static void put_string (int x_pos, int y_pos, char *message, int rq_attr)
{
	start_critical (7);
	printf ("%c[%d;%dH%c[%dp%s%c[%dp", ESC, y_pos, x_pos - 1, ESC, rq_attr, message, ESC, NORMAL);
	end_critical ();
}


static void draw_box (int stream, int x_cur, int y_cur, int height, int width)
{
	int loop;

	fprintf (stream, "%c(0", ESC);
	fprintf (stream, "%c[%d;%dHl", ESC, y_cur, x_cur);
	fprintf (stream, "%c[%d;%dHm", ESC, height + y_cur, x_cur);
	fprintf (stream, "%c[%d;%dHj", ESC, height + y_cur, width + x_cur);
	fprintf (stream, "%c[%d;%dHk", ESC, y_cur, width + x_cur);
	for (loop = x_cur + 1; loop < x_cur + width; loop++) {
		fprintf (stream, "%c[%d;%dHq", ESC, y_cur, loop);
		fprintf (stream, "%c[%d;%dHq", ESC, y_cur + height, loop);
	}
	for (loop = y_cur + 1; loop < y_cur + height; loop++) {
		fprintf (stream, "%c[%d;%dHx", ESC, loop, x_cur);
		fprintf (stream, "%c[%d;%dHx", ESC, loop, x_cur + width);
	}
	fprintf (stream, "%c(B", ESC);
}


static void heater_task (void)
{
	int adc_value;

	for (;;) {
		adc_value = adc ();
		temperature = (adc_value * 100) / 186;							/* 1 deg C = 1.86 units */
		if (temperature > target_temp)
			heater_off ();
		else
			heater_on ();
	}
}


static void update_rtc (void)
{
	if (++time.secs == 60) {
		time.secs = 0;
		if (++time.mins == 60) {
			time.mins = 0;
			if (++time.hours == 24)
				time.hours = 0;
		}
	}
}
