125 lines
2.7 KiB
C
125 lines
2.7 KiB
C
#include <stdint.h>
|
|
#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)
|
|
{
|
|
}
|