mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-27 10:00:17 -06:00
211 lines
5.8 KiB
C
211 lines
5.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (C) 2004-2022 Sam Demeulemeester
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include "io.h"
|
|
#include "string.h"
|
|
#include "serial.h"
|
|
#include "unistd.h"
|
|
|
|
#include "config.h"
|
|
#include "display.h"
|
|
|
|
static struct serial_port console_serial;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void serial_write_reg(struct serial_port *port, uint16_t reg, uint8_t val)
|
|
{
|
|
union {
|
|
uintptr_t addr;
|
|
uint8_t *ptr;
|
|
} reg_walker;
|
|
|
|
reg_walker.addr = port->base_addr + reg * port->reg_width;
|
|
|
|
if (port->is_mmio) {
|
|
*reg_walker.ptr = val;
|
|
} else {
|
|
__outb(val, reg_walker.addr);
|
|
}
|
|
}
|
|
|
|
static uint8_t serial_read_reg(struct serial_port *port, uint16_t reg)
|
|
{
|
|
union {
|
|
uintptr_t addr;
|
|
uint8_t *ptr;
|
|
} reg_walker;
|
|
|
|
reg_walker.addr = port->base_addr + reg * port->reg_width;
|
|
|
|
if (port->is_mmio) {
|
|
return *reg_walker.ptr;
|
|
} else {
|
|
return __inb(reg_walker.addr);
|
|
}
|
|
}
|
|
|
|
static void serial_wait_for_xmit(struct serial_port *port)
|
|
{
|
|
uint8_t lsr;
|
|
|
|
do {
|
|
lsr = serial_read_reg(port, UART_LSR);
|
|
} while ((lsr & BOTH_EMPTY) != BOTH_EMPTY);
|
|
}
|
|
|
|
void serial_echo_print(const char *p)
|
|
{
|
|
struct serial_port *port = &console_serial;
|
|
|
|
if (!port->enable) {
|
|
return;
|
|
}
|
|
/* Now, do each character */
|
|
while (*p) {
|
|
serial_wait_for_xmit(port);
|
|
|
|
/* Send the character out. */
|
|
serial_write_reg(port, UART_TX, *p);
|
|
if (*p==10) {
|
|
serial_wait_for_xmit(port);
|
|
serial_write_reg(port, UART_TX, 13);
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
|
|
void tty_print(int y, int x, const char *p)
|
|
{
|
|
static char sx[3], sy[3];
|
|
|
|
itoa(++x,sx);
|
|
itoa(++y,sy);
|
|
|
|
serial_echo_print("\x1b[");
|
|
serial_echo_print(sy);
|
|
serial_echo_print(";");
|
|
serial_echo_print(sx);
|
|
serial_echo_print("H");
|
|
serial_echo_print(p);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
void tty_init(void)
|
|
{
|
|
if (!enable_tty) {
|
|
return;
|
|
}
|
|
|
|
int uart_status, serial_div;
|
|
unsigned char lcr;
|
|
|
|
console_serial.enable = true;
|
|
console_serial.is_mmio = false;
|
|
console_serial.parity = SERIAL_DEFAULT_PARITY;
|
|
console_serial.bits = SERIAL_DEFAULT_BITS;
|
|
console_serial.baudrate = tty_params_baud;
|
|
console_serial.reg_width = 1;
|
|
console_serial.refclk = UART_REF_CLK;
|
|
console_serial.base_addr = serial_base_ports[tty_params_port];
|
|
|
|
/* read the Divisor Latch */
|
|
uart_status = serial_read_reg(&console_serial, UART_LCR);
|
|
serial_write_reg(&console_serial, UART_LCR, uart_status | UART_LCR_DLAB);
|
|
serial_read_reg(&console_serial, UART_DLM);
|
|
serial_read_reg(&console_serial, UART_DLL);
|
|
serial_write_reg(&console_serial, UART_LCR, uart_status);
|
|
|
|
/* now do hardwired init */
|
|
lcr = console_serial.parity | (console_serial.bits - 5);
|
|
serial_write_reg(&console_serial, UART_LCR, lcr); /* No parity, 8 data bits, 1 stop */
|
|
serial_div = (console_serial.refclk / console_serial.baudrate) / 16;
|
|
serial_write_reg(&console_serial, UART_LCR, 0x80|lcr); /* Access divisor latch */
|
|
serial_write_reg(&console_serial, UART_DLL, serial_div & 0xff); /* baud rate divisor */
|
|
serial_write_reg(&console_serial, UART_DLM, (serial_div >> 8) & 0xff);
|
|
serial_write_reg(&console_serial, UART_LCR, lcr); /* Done with divisor */
|
|
|
|
/* Prior to disabling interrupts, read the LSR and RBR registers */
|
|
uart_status = serial_read_reg(&console_serial, UART_LSR); /* COM? LSR */
|
|
uart_status = serial_read_reg(&console_serial, UART_RX); /* COM? RBR */
|
|
serial_write_reg(&console_serial, UART_IER, 0x00); /* Disable all interrupts */
|
|
|
|
tty_clear_screen();
|
|
tty_disable_cursor();
|
|
}
|
|
|
|
void tty_send_region(int start_row, int start_col, int end_row, int end_col)
|
|
{
|
|
char p[SCREEN_WIDTH+1];
|
|
int col_len = end_col - start_col;
|
|
|
|
if (start_col > (SCREEN_WIDTH - 1) || end_col > (SCREEN_WIDTH - 1)) {
|
|
return;
|
|
}
|
|
|
|
if (start_row > (SCREEN_HEIGHT - 1) || end_row > (SCREEN_HEIGHT - 1)) {
|
|
return;
|
|
}
|
|
|
|
for (int row = start_row; row <= end_row; row++) {
|
|
|
|
// Last line is inverted (Black on white)
|
|
if (row == SCREEN_HEIGHT-1) {
|
|
tty_inverse();
|
|
}
|
|
|
|
// Copy Shadow buffer to TTY buffer
|
|
for (int col = start_col; col <= end_col; col++) {
|
|
p[col] = shadow_buffer[row][col].value & 0x7F;
|
|
}
|
|
|
|
// Add string terminator
|
|
p[end_col+1] = '\0';
|
|
|
|
// For first line, title on top-left must be inverted
|
|
// Do the switch, send to TTY then continue to next line.
|
|
if (row == 0 && start_col == 0 && col_len > 28) {
|
|
tty_inverse();
|
|
p[28] = '\0';
|
|
tty_print(row,0,p);
|
|
tty_normal();
|
|
p[28] = '|';
|
|
tty_print(row, 28, p + 28);
|
|
continue;
|
|
}
|
|
|
|
// Replace degree symbol with '*' for tty to avoid a C437/VT100 translation table.
|
|
if (row == 1 && (shadow_buffer[1][25].value & 0xFF) == 0xF8) {
|
|
p[25] = 0x2A;
|
|
}
|
|
|
|
// Send row to TTY
|
|
tty_print(row, start_col, p + start_col);
|
|
|
|
// Revert to normal if last line.
|
|
if (row == SCREEN_HEIGHT-1) {
|
|
tty_normal();
|
|
}
|
|
}
|
|
}
|
|
|
|
char tty_get_key(void)
|
|
{
|
|
int uart_status = serial_read_reg(&console_serial, UART_LSR);
|
|
|
|
if (uart_status & UART_LSR_DR) {
|
|
return serial_read_reg(&console_serial, UART_RX);
|
|
} else {
|
|
return 0xFF;
|
|
}
|
|
}
|