#include #include "bios.h" #include "circ_buf.h" #define UART_CTC_CARD_BASE 0x20 #define CTC_BASE (UART_CTC_CARD_BASE + 0x00) #define CTC_CHANNEL_0 (CTC_BASE + 0) #define CTC_CHANNEL_1 (CTC_BASE + 1) #define CTC_CHANNEL_2 (CTC_BASE + 2) #define CTC_CHANNEL_3 (CTC_BASE + 3) #define UART_BASE (UART_CTC_CARD_BASE + 0x04) #define UART_CONTROL (UART_BASE + 0) #define UART_DATA (UART_BASE + 1) static volatile uint8_t uart_tx_busy; static volatile circular_buffer_t rx_buf; static volatile circular_buffer_t tx_buf; static void uart_kick(void) { char c; disable_interrupts(); if (!uart_tx_busy && buffer_dequeue(&tx_buf, &c)) { uart_tx_busy = 1; io_out(UART_DATA, c); io_out(UART_CONTROL, 0b10110101); } enable_interrupts(); } void bios_putc(char c) { uint8_t queued; do { disable_interrupts(); queued = buffer_enqueue(&tx_buf, c); enable_interrupts(); if (!queued) uart_kick(); } while (!queued); uart_kick(); } void bios_puts(const char *s) { while (*s) bios_putc(*s++); } uint8_t bios_try_getc(void) { char c; uint8_t received; disable_interrupts(); received = buffer_dequeue(&rx_buf, &c); enable_interrupts(); return received ? (uint8_t)c : 0; } char bios_getc(void) { uint8_t c; do { c = bios_try_getc(); } while (!c); return (char)c; } void bios_flush(void) { while (uart_tx_busy || !buffer_is_empty(&tx_buf)) uart_kick(); } void bios_init(void) { buffer_init(&rx_buf); buffer_init(&tx_buf); /* A CPU-only reset does not necessarily reset the external CTC. Stop and disable every channel before rebuilding the UART clock/IRQ path. */ io_out(CTC_CHANNEL_0, 0x03); io_out(CTC_CHANNEL_1, 0x03); io_out(CTC_CHANNEL_2, 0x03); io_out(CTC_CHANNEL_3, 0x03); /* The 6850 has no reset input; CR1:CR0 = 11 is its master reset. */ io_out(UART_CONTROL, 0b00000011); io_out(CTC_CHANNEL_0, 0x57); io_out(CTC_CHANNEL_0, 13); io_out(CTC_CHANNEL_3, 0b11001111); io_out(CTC_CHANNEL_3, 1); io_out(CTC_CHANNEL_0, 0x00); io_out(CTC_CHANNEL_0, 0x40); uart_tx_busy = 0; io_out(UART_CONTROL, 0b10010101); enable_interrupts(); } void uart_interrupt(void) { uint8_t status = io_in(UART_CONTROL); if ((status & 0x21) != 0) { char c = io_in(UART_DATA); if ((status & 0x01) != 0) buffer_enqueue(&rx_buf, c); } if ((status & 0x02) != 0 && uart_tx_busy) { char c; if (buffer_dequeue(&tx_buf, &c)) { io_out(UART_DATA, c); } else { io_out(UART_CONTROL, 0b10010101); uart_tx_busy = 0; } } } void ctc_interrupt(void) { }