/*
 * 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 file implements a set of routines to interface with the
 * DUART chip on the Flight Board.
 *
 * (C) Copyright Rich Teer 1990, 2023, 2026.
 */


#include <varargs.h>
#include "duart.h"
#define TX_MASK 0x04
#define RX_MASK 0x01
#define ESC 0x1B
#define boolean int
#define FALSE 0
#define TRUE 1
#define abs(number) (number < 0) ? -number : number

static duart_t *duart = (duart_t *)0xA00000;
static boolean stdin_semaphore = FALSE;
static boolean stdout_semaphore = FALSE;
static boolean auxin_semaphore = FALSE;
static boolean auxout_semaphore = FALSE;


void duart_init (void)													/* Initialises the DUART chip */
{
	int baud_rate;														/* The Baud rate to use */
	int dummy;															/* Character read in */

	duart -> isr_imr.imr = 0x00;										/* Mask all interrupts */
	duart -> ipcr_acr.acr = 0xB0;										/* Select Baud set 2, timer & interrupts off */
	duart -> scc_bsc.bsc = 0x03;										/* Assert RTS lines */
	duart -> ft_cra.cra = 0x10;											/* Reset Mode Register pointers to */
	duart -> ft_crb.crb = 0x10;											/* Mode Register 1 */
	duart -> mra = 0x13;												/* Select data format - */
	duart -> mrb = 0x13;												/* 8 data bits, 1 stop bit, no parity, CTS enabled */
	duart -> mra = 0x17;												/* Continuing setting up */
	duart -> mrb = 0x17;												/* the data format (refering to MR2A & MR2B now) */
	duart -> ft_cra.cra = 0x05;											/* Enable transmit */
	duart -> ft_crb.crb = 0x05;											/* and receive */
	baud_rate = 0xCC;													/* Try 19200 baud first */
	for (;;) {															/* Do forever ... */
		duart -> sra_csra.csra = baud_rate;								/* Set Baud rate for Port A */
		duart -> srb_csrb.csrb = baud_rate;								/* Set Baud rate for Port B */
		dummy = getchar ();												/* Get a character and junk it */
		dummy = getchar ();												/* Get another character */
		if (dummy == (unsigned short) 0x0D)								/* If read character is carriage return, */
			break;														/* We've finished */
		else
			baud_rate = (baud_rate - 0x11 < 0x00) ? 0xCC : baud_rate - 0x11;	/* Try next baud rate */
	}
}


static void claim_stdin (void)
{
	while (stdin_semaphore)
		;
	stdin_semaphore = TRUE;
}

static void release_stdin (void)
{
	stdin_semaphore = FALSE;
}


static void claim_stdout (void)
{
	while (stdout_semaphore)
		;
	stdout_semaphore = TRUE;
}


static void release_stdout (void)
{
	stdout_semaphore = FALSE;
}


static void claim_auxin (void)
{
	while (auxin_semaphore)
		;
	auxin_semaphore = TRUE;
}


static void release_auxin (void)
{
	auxin_semaphore = FALSE;
}


static void claim_auxout (void)
{
	while (auxout_semaphore)
		;
	auxout_semaphore = TRUE;
}


static void release_auxout (void)
{
	auxout_semaphore = FALSE;
}


int getc (int stream)
{
	unsigned short dummy;

	if (stream == stdin) {
		while (((dummy = duart->sra_csra.sra) & RX_MASK) == 0)
			;															/* Wait for receive buffer to fill */
		dummy = duart->rba_tba.rba  ;									/* Get character from Receive Buffer A */
		return (dummy & 0x7F);											/* Return character stripped of parity bit */
	} else {
		while (((dummy = duart->srb_csrb.srb) & RX_MASK) == 0)
			;															/* Wait for receive buffer to fill */
		dummy = duart->rbb_tbb.rbb;										/* Get character from Receive Buffer B */
		return (dummy & 0x7F);											/* Return character stripped of parity bit */
	}
}


int putc (int c, int stream)
{
	unsigned short dummy;

	if (stream == stdout) {
		while (((dummy = duart -> sra_csra.sra) & TX_MASK) == 0)
			;															/* Wait for transmit buffer to empty */
		if (c == (unsigned short) 0x0D) {
			duart->rba_tba.tba = (unsigned short) 0x0A;					/* CR pressed, so output a line feed first */
			while (((dummy = duart->sra_csra.sra) & TX_MASK) == 0)
				;														/* Wait for transmit buffer to empty */
		} else {
			if (c == (unsigned short) 0x0A) {
				duart->rba_tba.tba = (unsigned short) 0x0D;
				while (((dummy = duart->sra_csra.sra) & TX_MASK) == 0);	/* Wait for transmit buffer to empty */
			}
		}
		duart->rba_tba.tba = c;											/* Output the character */
	} else {
		while (((dummy = duart->srb_csrb.srb) & TX_MASK) == 0)
			;															/* Wait for transmit buffer to empty */
		if (c == (unsigned short) 0x0D) {
			duart->rbb_tbb.tbb = (unsigned short) 0x0A;					/* CR pressed, so output a line feed first */
			while (((dummy = duart->srb_csrb.srb) & TX_MASK) == 0)
				;														/* Wait for transmit buffer to empty */
		} else {
			if (c == (unsigned short) 0x0A) {
				duart->rbb_tbb.tbb = (unsigned short) 0x0D;
				while (((dummy = duart->srb_csrb.srb) & TX_MASK) == 0)
					;													/* Wait for transmit buffer to empty */
			}
		}
		duart->rbb_tbb.tbb = c;											/* Output the character */
	}
}


int getc_nw (int stream)
{
	unsigned short dummy;

	if (stream == stdin) {
		if (((dummy = duart->sra_csra.sra) & RX_MASK) == 0)
			return (-1);
		dummy = duart->rba_tba.rba;
		return (dummy & 0x7F);
	} else {
		if (((dummy = duart->srb_csrb.srb) & RX_MASK) == 0)
			return (-1);
		dummy = duart->rbb_tbb.rbb;
		return (dummy & 0x7F);
	}
}


int getchar (void)														/* Returns the next character typed (unbuffered) */
{
	unsigned short dummy;

	while (((dummy = duart->sra_csra.sra) & RX_MASK) == 0)
		;																/* Wait for receive buffer to fill */
	dummy = duart->rba_tba.rba;											/* Get character from Receive Buffer A */
	return (dummy & 0x7F);												/* Return character stripped of parity bit */
}


void putchar (unsigned short character)									/* Outputs the character "character" to the terminal */
{
	unsigned short dummy;

	while (((dummy = duart->sra_csra.sra) & TX_MASK) == 0)
		;																/* Wait for transmit buffer to empty */
	if (character == (unsigned short) 0x0D) {
		duart->rba_tba.tba = (unsigned short) 0x0A;						/* CR pressed, so output a line feed first */
		while (((dummy = duart->sra_csra.sra) & TX_MASK) == 0);			/* Wait for transmit buffer to empty */
	} else {
		if (character == (unsigned short) 0x0A) {
			duart -> rba_tba.tba = (unsigned short) 0x0D;
			while (((dummy = duart->sra_csra.sra) & TX_MASK) == 0)
				;														/* Wait for transmit buffer to empty */
	}
	duart->rba_tba.tba = character;										/* Output the character */
}


void puts (char *text)
{
	fputs (text, stdout);
}


void fputs (char *text, int stream)										/* Prints the NULL terminated string on the screen */
{
	while (*text)
		putc ((unsigned short) *text++, stream);						/* Print the characters until '\0' is reached */
}


void gets (char *string)												/* Gets a string, terminated by CR from keyboard */
{
	int index = 0;														/* Array index = 0 */
	int loop;
	unsigned short c;													/* Current character */

	c = getchar ();
	while ((c != 0x0D) && (c != 0x04)) {
		switch (c) {
			case 0x17:													/* ^W - delete word */
				while ((string [index] != ' ') && (string [index] != '\t') && (index > 0)) {
					delete (stdout);
					index--;
				}
				if (index > 0) {
					delete (stdout);
					index--;
				}
				break;
			case 0x15:													/* ^U - delete line */
				for (loop = 0; loop != index; loop++)
					delete (stdout);									/* Write a delete char */
				index = 0;
				break;
			case 0x7F:													/* Delete key */
				if (index > 0) {
					delete (stdout);
					index--;
				}
				break;
			default:
				string[index++] = c;
				putchar (c);											/* Echo the character back to the terminal */
				break;
		}
		c = getchar ();
	}
	putchar (0x0D);
	string[index] = 0x00;												/* Add the NULL terminator */
}

void fgets (int stream, char *string)									/* Gets a string, terminated by CR from keyboard */
{
	int index = 0;														/* Array index = 0 */
	int loop;
	unsigned short c;													/* Current character */

	c = getc (stream);
	while ((c != 0x0D) && (c != 0x04)) {
		switch (c) {
			case 0x17:													/* ^W - delete word */
				while ((string [index] != ' ') && (string [index] != '\t') && (index > 0)) {
					delete (stream);
					index--;
				}
				if (index > 0) {
					delete (stream);
					index--;
				}
				break;
			case 0x15:													/* ^U - delete line */
				for (loop = 0; loop != index; loop++)
					delete (stream);									/* Write a delete char */
				index = 0;
				break;
			case 0x7F:													/* Delete key */
				if (index > 0) {
					delete (stream);
					index--;
				}
				break;
			default:
				string [index++] = c;
				putc (c, stream);										/* Echo the character back to the terminal */
				break;
		}
		c = getc (stream);
	}
	putc (0x0D, stream);
	string[index] = 0x00;												/* Add the NULL terminator */
}


void scanf (va_dcl va_alist)
{
	va_list args;														/* The list of arguments */
	char *format;														/* The format string */
	char *ptr;															/* Where we are in the format string */
	char *str_arg;														/* A string argument */
	int *int_arg;														/* An integer argument */
	double float_arg;													/* A float or double argument */
	char output_string[1024];											/* The text finally printed */
	static char char_str[] = " ";
	char string1[33];													/* First string used for number conversion */
	int index1;															/* First array index used for number conversion */
	int index2;															/* Second number used for number conversion */
	int digit;															/* The digit's that's currently being converted */
	int factor;
	int modulus;
	int loop;
	int length;
	int number;
	int final_number;
	int place_value;
	boolean lead_zero;

	output_string[0] = '\0';
	va_start (args);													/* Initialise arg list */
	format = va_arg (args, char *);										/* Get the format first */
	for (ptr = format; *ptr; ptr++) {
		if (*ptr == '%') {
			switch (*++ptr) {
				case 's':
					str_arg = va_arg (args, char *);
					gets (str_arg);
					break;
				case 'c':
					int_arg = (int *)va_arg (args, int *);
					*int_arg = (char)getchar ();
					break;
				case 'd':
					int_arg = (int *)va_arg (args, int *);
					number = 0;
					final_number = 0;
					place_value = 1;
					gets (string1);
					length = strlen (string1) - 1;
					for (loop = length; loop >= 0; loop--) {
						number = (string1[loop] - 48) * place_value;
						final_number += number;
						place_value *= 10;
					}
					*int_arg = final_number;
					break;
				case 'x':
				case 'b':
				case 'o':
				case 'f':
				case '%':
				default:
					puts ("printf: Illegal format\n");
					break;
			}
		}
	}
	va_end (args);														/* Clean up argument list */
}


void fscanf (int stream, va_dcl va_alist)
{
	va_list args;														/* The list of arguments */
	char *format;														/* The format string */
	char *ptr;															/* Where we are in the format string */
	char *str_arg;														/* A string argument */
	int *int_arg;														/* An integer argument */
	double float_arg;													/* A float or double argument */
	char output_string[1024];											/* The text finally printed */
	static char char_str[] = " ";
	char string1[33];													/* First string used for number conversion */
	int index1;															/* First array index used for number conversion */
	int index2;															/* Second number used for number conversion */
	int digit;															/* The digit's that's currently being converted */
	int factor;
	int modulus;
	int loop;
	int length;
	int number;
	int final_number;
	int place_value;
	boolean lead_zero;

	output_string[0] = '\0';
	va_start (args);													/* Initialise arg list */
	format = va_arg (args, char *);										/* Get the format first */
	for (ptr = format; *ptr; ptr++) {
		if (*ptr == '%') {
			switch (*++ptr) {
				case 's':
					str_arg = va_arg (args, char *);
					fgets (stream, str_arg);
					break;
				case 'c':
					int_arg = (int *)va_arg (args, int *);
					*int_arg = (char)getchar ();
					break;
				case 'd':
					int_arg = (int *)va_arg (args, int *);
					number = 0;
					final_number = 0;
					place_value = 1;
					fgets (stream, string1);
					length = strlen (string1) - 1;
					for (loop = length; loop >= 0; loop--) {
						number = (string1[loop] - 48) * place_value;
						final_number += number;
						place_value *= 10;
					}
					*int_arg = final_number;
					break;
				case 'x':
				case 'b':
				case 'o':
				case 'f':
				case '%':
				default:
					puts ("printf: Illegal format\n");
					break;
			}
		}
	}
	va_end (args);														/* Clean up argument list */
}


printf (va_dcl va_alist)
{
	va_list args;														/* The list of arguments */
	char *format;														/* The format string */
	char *ptr;															/* Where we are in the format string */
	char *str_arg;														/* A string argument */
	int int_arg;														/* An integer argument */
	int char_arg;														/* A character argument */
	double float_arg;													/* A float or double argument */
	char output_string[1024];											/* The text finally printed */
	static char char_str[] = " ";
	char string1[33];													/* First string used for number conversion */
	int index1;															/* First array index used for number conversion */
	int index2;															/* Second number used for number conversion */
	int digit;															/* The digit's that's currently being converted */
	int factor;
	int modulus;
	boolean lead_zero;

	output_string[0] = '\0';
	va_start (args);													/* Initialise arg list */
	format = va_arg (args, char *);										/* Get the format first */
	for (ptr = format; *ptr; ptr++) {
		if (*ptr == '%') {
			switch (*++ptr) {
				case 's':
					str_arg = va_arg (args, char *);
					strcat (output_string, str_arg);
					break;
				case 'c':
					*char_str = va_arg (args, int);
					strcat (output_string, char_str);
					break;
				case 'd':
					int_arg = va_arg (args, int);
					*string1 = '\0';
					if (int_arg < 0) {
						strcat (output_string, "-");
						int_arg = abs (int_arg);
					}
					itos (int_arg, 10, string1);
					strcat (output_string, string1);
					break;
				case 'x':
					int_arg = va_arg (args, int);
					*string1 = '\0';
					if (int_arg < 0) {
						strcat (output_string, "-");
						int_arg = abs (int_arg);
					}
					itos (int_arg, 16, string1);
					strcat (output_string, string1);
					break;
				case 'b':
					int_arg = va_arg (args, int);
					*string1 = '\0';
					if (int_arg < 0) {
						strcat (output_string, "-");
						int_arg = abs (int_arg);
					}
					itos (int_arg, 2, string1);
					strcat (output_string, string1);
					break;
				case 'o':
					int_arg = va_arg (args, int);
					*string1 = '\0';
					if (int_arg < 0) {
						strcat (output_string, "-");
						int_arg = abs (int_arg);
					}
					itos (int_arg, 8, string1);
					strcat (output_string, string1);
					break;
				case 'f':
					float_arg = va_arg (args, double);
					*string1 = '\0';
					if (float_arg < 0.0) {
						strcat (output_string, "-");
						float_arg = abs (int_arg);
					}
					ftos (float_arg, 6, string1);
					strcat (output_string, string1);
					break;
				case '%':
					strcat (output_string, "%");
					break;
				default:
					puts ("printf: Illegal format\n");
					break;
			}
		} else {
			*char_str = *ptr;
			strcat (output_string, char_str);
		}
	}
	puts (output_string);
	va_end (args);														/* Clean up argument list */
}


void fprintf (int stream, va_dcl va_alist)
{
	va_list args;														/* The list of arguments */
	char *format;														/* The format string */
	char *ptr;															/* Where we are in the format string */
	char *str_arg;														/* A string argument */
	int int_arg;														/* An integer argument */
	int char_arg;														/* A character argument */
	double float_arg;													/* A float or double argument */
	char output_string[1024];											/* The text finally printed */
	static char char_str[] = " ";
	char string1[33];													/* First string used for number conversion */
	int index1;															/* First array index used for number conversion */
	int index2;															/* Second number used for number conversion */
	int digit;															/* The digit's that's currently being converted */
	int factor;
	int modulus;
	boolean lead_zero;

	output_string[0] = '\0';
	va_start (args);													/* Initialise arg list */
	format = va_arg (args, char *);										/* Get the format first */
	for (ptr = format; *ptr; ptr++) {
		if (*ptr == '%') {
			switch (*++ptr) {
				case 's':
					str_arg = va_arg (args, char *);
					strcat (output_string, str_arg);
					break;
				case 'c':
					*char_str = va_arg (args, int);
					strcat (output_string, char_str);
					break;
				case 'd':
					int_arg = va_arg (args, int);
					*string1 = '\0';
					if (int_arg < 0) {
						strcat (output_string, "-");
						int_arg = abs (int_arg);
					}
					itos (int_arg, 10, string1);
					strcat (output_string, string1);
					break;
				case 'x':
					int_arg = va_arg (args, int);
					*string1 = '\0';
					if (int_arg < 0) {
						strcat (output_string, "-");
						int_arg = abs (int_arg);
					}
					itos (int_arg, 16, string1);
					strcat (output_string, string1);
					break;
				case 'b':
					int_arg = va_arg (args, int);
					*string1 = '\0';
					if (int_arg < 0) {
						strcat (output_string, "-");
						int_arg = abs (int_arg);
					}
					itos (int_arg, 2, string1);
					strcat (output_string, string1);
					break;
				case 'o':
					int_arg = va_arg (args, int);
					*string1 = '\0';
					if (int_arg < 0) {
						strcat (output_string, "-");
						int_arg = abs (int_arg);
					}
					itos (int_arg, 8, string1);
					strcat (output_string, string1);
					break;
				case 'f':
					float_arg = va_arg (args, double);
					*string1 = '\0';
					if (float_arg < 0.0) {
						strcat (output_string, "-");
						float_arg = abs (int_arg);
					}
					ftos (float_arg, 6, string1);
					strcat (output_string, string1);
					break;
				case '%':
					strcat (output_string, "%");
					break;
				default:
					puts ("printf: Illegal format\n");
					break;
			}
		} else {
			*char_str = *ptr;
			strcat (output_string, char_str);
		}
	}
	fputs (output_string, stream);
	va_end (args);														/* Clean up argument list */
}


static void delete (int stream)											/* Deletes a character - no scope outside this file */
{
	putc (0x08, stream);												/* Write a backspace character */
	putc (' ', stream);													/* Write a space */
	putc (0x08, stream);												/* Write a backspace character */
}


void cls (void)
{
	printf ("%c[2J%c[;H", ESC, ESC);
}


void cursor (int x, int y)
{
	printf ("%c[%d;%dH", ESC, y, x);
}


void centre_132 (char *text, int y)
{
	cursor (66 - (strlen (text) / 2), y);
	printf (text);
}


void centre_80 (char *text, int y)
{
	cursor (40 - (strlen (text) / 2), y);
	printf (text);
}
