/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see .
uart.h
A simple serial terminal driver.
******************************************************************************/
#include "platform.h"
#include
#include
#ifdef PLATFORM_SERIAL
#include "vga.h"
#endif
#include "uart.h"
using namespace Maxsi;
namespace Sortix
{
namespace UART
{
const nat TXR = 0; // Transmit register
const nat RXR = 0; // Receive register
const nat IER = 1; // Interrupt Enable
const nat IIR = 2; // Interrupt ID
const nat FCR = 2; // FIFO control
const nat LCR = 3; // Line control
const nat MCR = 4; // Modem control
const nat LSR = 5; // Line Status
const nat MSR = 6; // Modem Status
const nat DLL = 0; // Divisor Latch Low
const nat DLM = 1; // Divisor latch High
const nat LCR_DLAB = 0x80; // Divisor latch access bit
const nat LCR_SBC = 0x40; // Set break control
const nat LCR_SPAR = 0x20; // Stick parity (?)
const nat LCR_EPAR = 0x10; // Even parity select
const nat LCR_PARITY = 0x08; // Parity Enable
const nat LCR_STOP = 0x04; // Stop bits: 0=1 bit, 1=2 bits
const nat LCR_WLEN5 = 0x00; // Wordlength: 5 bits
const nat LCR_WLEN6 = 0x01; // Wordlength: 6 bits
const nat LCR_WLEN7 = 0x02; // Wordlength: 7 bits
const nat LCR_WLEN8 = 0x03; // Wordlength: 8 bits
const nat LSR_TEMT = 0x40; // Transmitter empty
const nat LSR_THRE = 0x20; // Transmit-hold-register empty
const nat LSR_READY = 0x1;
const nat Port = 0x3f8;
const nat BASE_BAUD = 1843200/16;
const nat BOTH_EMPTY = LSR_TEMT | LSR_THRE;
#ifdef SORTIX_VGA_H
VGA::Frame VGALastFrame;
#endif
nat ProbeBaud(nat Port)
{
uint8_t lcr = CPU::InPortB(Port + LCR);
CPU::OutPortB(Port + LCR, lcr | LCR_DLAB);
uint8_t dll = CPU::InPortB(Port + DLL);
uint8_t dlm = CPU::InPortB(Port + DLM);
CPU::OutPortB(Port + LCR, lcr);
nat quot = (dlm << 8) | dll;
return BASE_BAUD / quot;
}
void WaitForEmptyBuffers(nat Port)
{
while ( true )
{
nat Status = CPU::InPortB(Port + LSR);
if ( (Status & BOTH_EMPTY) == BOTH_EMPTY )
{
return;
}
}
}
nat Baud;
void Init()
{
#ifdef SORTIX_VGA_H
InvalidateVGA();
#endif
#ifdef JSSORTIX
// This crashes the JS VM, so don't do it.
return;
#endif
Baud = ProbeBaud(Port);
CPU::OutPortB(Port + LCR, 0x3); // 8n1
CPU::OutPortB(Port + IER, 0); // No interrupt
CPU::OutPortB(Port + FCR, 0); // No FIFO
CPU::OutPortB(Port + MCR, 0x3); // DTR + RTS
nat Divisor = 115200 / Baud;
uint8_t C = CPU::InPortB(Port + LCR);
CPU::OutPortB(Port + LCR, C | LCR_DLAB);
CPU::OutPortB(Port + DLL, Divisor & 0xFF);
CPU::OutPortB(Port + DLM, (Divisor >> 8) & 0xFF);
CPU::OutPortB(Port + LCR, C & ~LCR_DLAB);
}
void Read(uint8_t* Buffer, size_t Size)
{
// Save the IER and disable interrupts.
nat ier = CPU::InPortB(Port + IER);
CPU::OutPortB(Port + IER, 0);
for ( size_t I = 0; I < Size; I++ )
{
while ( ! ( CPU::InPortB(Port + LSR) & LSR_READY ) ) { }
Buffer[I] = CPU::InPortB(Port);
}
// Wait for transmitter to become empty and restore the IER.
WaitForEmptyBuffers(Port);
CPU::OutPortB(Port + IER, ier);
}
void Write(const void* B, size_t Size)
{
const uint8_t* Buffer = (const uint8_t*) B;
// Save the IER and disable interrupts.
nat ier = CPU::InPortB(Port + IER);
CPU::OutPortB(Port + IER, 0);
for ( size_t I = 0; I < Size; I++ )
{
WaitForEmptyBuffers(Port);
CPU::OutPortB(Port, Buffer[I]);
}
// Wait for transmitter to become empty and restore the IER.
WaitForEmptyBuffers(Port);
CPU::OutPortB(Port + IER, ier);
}
void WriteChar(char C)
{
// Save the IER and disable interrupts.
nat ier = CPU::InPortB(Port + IER);
CPU::OutPortB(Port + IER, 0);
WaitForEmptyBuffers(Port);
CPU::OutPortB(Port, C);
// Wait for transmitter to become empty and restore the IER.
WaitForEmptyBuffers(Port);
CPU::OutPortB(Port + IER, ier);
}
int TryPopChar()
{
// Save the IER and disable interrupts.
nat ier = CPU::InPortB(Port + IER);
CPU::OutPortB(Port + IER, 0);
int Result = -1;
if ( CPU::InPortB(Port + LSR) & LSR_READY )
{
Result = CPU::InPortB(Port);
}
// Wait for transmitter to become empty and restore the IER.
WaitForEmptyBuffers(Port);
CPU::OutPortB(Port + IER, ier);
return Result;
}
void WriteNumberAsString(uint8_t Num)
{
if ( Num > 100 ) { WriteChar(Num / 100); }
if ( Num > 10 ) { WriteChar(Num / 10); }
WriteChar(Num % 10);
}
#ifdef SORTIX_VGA_H
// Change from VGA color to another color system.
nat ConversionTable[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 0, 4, 2, 6, 1, 5, 3, 7 };
void InvalidateVGA()
{
for ( nat I = 0; I < VGALastFrame.Width * VGALastFrame.Height; I++ ) { VGALastFrame.Data[I] = 0; }
}
void RenderVGA(const VGA::Frame* Frame)
{
const uint16_t* Source = Frame->Data;
nat LastColor = 1337;
nat SkippedSince = 0;
bool posundefined = true;
for ( nat Y = 0; Y < Frame->Height; Y++)
{
for ( nat X = 0; X < Frame->Width; X++ )
{
nat Index = Y * Frame->Width + X;
nat Element = Source[Index];
nat OldElement = VGALastFrame.Data[Index];
if ( Element == OldElement ) { continue; }
// Update the position if we skipped some characters.
if ( Index - SkippedSince > 8 || posundefined )
{
const nat LineId = Y + 1;
const nat ColumnId = X + 1;
if ( ColumnId > 1 )
{
UART::WriteChar('\e');
UART::WriteChar('[');
UART::WriteChar('0' + LineId / 10);
UART::WriteChar('0' + LineId % 10);
UART::WriteChar(';');
UART::WriteChar('0' + ColumnId / 10);
UART::WriteChar('0' + ColumnId % 10);
UART::WriteChar('H');
}
else
{
UART::WriteChar('\e');
UART::WriteChar('[');
UART::WriteChar('0' + LineId / 10);
UART::WriteChar('0' + LineId % 10);
UART::WriteChar('H');
}
SkippedSince = Index;
posundefined = false;
}
for ( nat Pos = SkippedSince; Pos <= Index; Pos++ )
{
Element = Source[Pos];
OldElement = VGALastFrame.Data[Pos];
nat NewColor = (ConversionTable[ (Element >> 12) & 0xF ] << 3) | (ConversionTable[ (Element >> 8) & 0xF ]);
// Change the color if we need to.
if ( LastColor != NewColor )
{
nat OldFGColor = LastColor % 8;
nat OldBGColor = LastColor / 8;
nat FGColor = NewColor % 8;
nat BGColor = NewColor / 8;
if ( LastColor == 1337 ) { OldFGColor = 9; OldBGColor = 9; }
if ( (OldFGColor != FGColor) && (OldBGColor != BGColor) )
{
UART::WriteChar('\e');
UART::WriteChar('[');
UART::WriteChar('3');
UART::WriteChar('0' + FGColor);
UART::WriteChar(';');
UART::WriteChar('4');
UART::WriteChar('0' + BGColor);
UART::WriteChar('m');
}
else if ( OldFGColor != FGColor )
{
UART::WriteChar('\e');
UART::WriteChar('[');
UART::WriteChar('3');
UART::WriteChar('0' + FGColor);
UART::WriteChar('m');
}
else if ( OldBGColor != BGColor )
{
UART::WriteChar('\e');
UART::WriteChar('[');
UART::WriteChar('4');
UART::WriteChar('0' + BGColor);
UART::WriteChar('m');
}
LastColor = NewColor;
}
VGALastFrame.Data[Pos] = Element;
Element &= 0x7F;
// Filter away any non-printable characters.
if ( Element < 32 || Element > 126 ) { Element = '?'; }
UART::WriteChar(Element);
}
SkippedSince = Index + 1;
}
}
}
#endif
}
}