328 lines
10 KiB
C#
328 lines
10 KiB
C#
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: HalScreen.cs
|
|
//
|
|
// Note:
|
|
// The Indispensable PC Hardware Book (Third Edition), pp1055-1066.
|
|
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using Microsoft.Singularity.Channels;
|
|
using Microsoft.Singularity.Io;
|
|
using Microsoft.Singularity.Configuration;
|
|
|
|
namespace Microsoft.Singularity.Hal
|
|
{
|
|
// declare resources for the kernel manifest
|
|
[DriverCategory]
|
|
public sealed class HalScreenResources : DriverCategoryDeclaration
|
|
{
|
|
[IoFixedPortRange(Base = 0x03d4, Length = 0x01, Shared = true)]
|
|
public IoPortRange indexPort;
|
|
|
|
[IoFixedPortRange(Base = 0x03d5, Length = 0x01, Shared = true)]
|
|
public IoPortRange dataPort;
|
|
|
|
[IoFixedMemoryRange(Base = 0xb8000, Length = 8000, Shared = true)]
|
|
public IoMemoryRange screenMemory;
|
|
|
|
//[TODO] - how can we represent endpoints here?
|
|
//[ServiceEndpoint(typeof(ConsoleDeviceContract.Exp),
|
|
// CustomName = "HalScreen")]
|
|
//TRef<ServiceProviderContract.Exp:Start> conoutsp;
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
public class HalScreen
|
|
{
|
|
public const int Columns = 80;
|
|
public const int Rows = 50;
|
|
public const int ScreenSize = Columns * Rows;
|
|
|
|
public const ushort CGA_INDEX_PORT = 0x3d4;
|
|
public const ushort CGA_DATA_PORT = 0x3d5;
|
|
public const byte SmallCursorStart = 6;
|
|
public const byte LargeCursorStart = 0;
|
|
|
|
private enum Coloring
|
|
{
|
|
Dim = 0x1700,
|
|
Yellow = 0x1e00,
|
|
Green = 0x1a00,
|
|
Red = 0x1c00,
|
|
Normal = 0x1f00
|
|
}
|
|
|
|
private ushort color = (ushort)Coloring.Normal;
|
|
private int cursor = 0;
|
|
private bool cursorVisible = false;
|
|
private byte cursorStartLine = SmallCursorStart;
|
|
|
|
private const byte CGA_CURSOR_START = 0xa;
|
|
private const byte CGA_CURSOR_MSB = 0xe;
|
|
private const byte CGA_CURSOR_LSB = 0xf;
|
|
|
|
private IoPort indexRegister = null;
|
|
private IoPort dataRegister = null;
|
|
private IoMemory screenBuffer = null;
|
|
|
|
public HalScreen(IoConfig config)
|
|
{
|
|
indexRegister =
|
|
((IoPortRange) config.FixedRanges[0]).PortAtOffset(
|
|
0, 1, Access.Write);
|
|
dataRegister =
|
|
((IoPortRange) config.FixedRanges[1]).PortAtOffset(
|
|
0, 1, Access.Write);
|
|
screenBuffer =
|
|
((IoMemoryRange) config.FixedRanges[2]).MemoryAtOffset(
|
|
0, ScreenSize * 2, Access.ReadWrite);
|
|
Clear();
|
|
WriteLine("Singularity HAL Console Driver.");
|
|
UpdateCursor(true);
|
|
}
|
|
|
|
public HalScreen()
|
|
{
|
|
// For compatibility with .csi compiled file
|
|
DebugStub.Break();
|
|
}
|
|
|
|
public void Dim()
|
|
{
|
|
color = (ushort)Coloring.Dim;
|
|
}
|
|
|
|
public void Yellow()
|
|
{
|
|
color = (ushort)Coloring.Yellow;
|
|
}
|
|
|
|
public void Green()
|
|
{
|
|
color = (ushort)Coloring.Green;
|
|
}
|
|
|
|
public void Red()
|
|
{
|
|
color = (ushort)Coloring.Red;
|
|
}
|
|
|
|
public void Normal()
|
|
{
|
|
color = (ushort)Coloring.Normal;
|
|
}
|
|
|
|
public void CursorFlash()
|
|
{
|
|
screenBuffer.Write16(0, (ushort)(screenBuffer.Read16(0) ^ 0xff00u));
|
|
}
|
|
|
|
public void CursorHide()
|
|
{
|
|
UpdateCursor(false);
|
|
}
|
|
|
|
public void CursorShow()
|
|
{
|
|
UpdateCursor(true);
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public void Clear()
|
|
{
|
|
if (screenBuffer == null) {
|
|
DebugStub.Break();
|
|
return;
|
|
}
|
|
ushort zip = (ushort)(color | ' ');
|
|
IoResult result = screenBuffer.Write16NoThrow(0, zip, ScreenSize);
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
|
|
cursor = 0;
|
|
UpdateCursor(cursorVisible);
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public void GetDisplayDimensions(out int columns, out int rows)
|
|
{
|
|
columns = Columns;
|
|
rows = Rows;
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public void GetCursorPosition(out int column, out int row)
|
|
{
|
|
column = cursor % Columns;
|
|
row = cursor / Columns;
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public void SetCursorSizeLarge()
|
|
{
|
|
cursorStartLine = LargeCursorStart;
|
|
ForcedCursorUpdate();
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public void SetCursorSizeSmall()
|
|
{
|
|
cursorStartLine = SmallCursorStart;
|
|
ForcedCursorUpdate();
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
private void ForcedCursorUpdate()
|
|
{
|
|
bool saved = cursorVisible;
|
|
cursorVisible = !cursorVisible;
|
|
UpdateCursor(saved);
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
private void UpdateCursor(bool newVisibleState)
|
|
{
|
|
IoResult result;
|
|
|
|
if (newVisibleState != cursorVisible) {
|
|
byte cgaStart = 32; // Cursor off
|
|
if (newVisibleState) {
|
|
cgaStart = (byte)(64 + cursorStartLine); // Cursor on
|
|
}
|
|
|
|
result = indexRegister.Write8NoThrow(CGA_CURSOR_START);
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
|
|
result = dataRegister.Write8NoThrow(cgaStart);
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
|
|
cursorVisible = newVisibleState;
|
|
}
|
|
|
|
if (newVisibleState) {
|
|
// Write cursor location
|
|
result = indexRegister.Write8NoThrow(CGA_CURSOR_MSB);
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
result = dataRegister.Write8NoThrow((byte)(cursor >> 8));
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
result = indexRegister.Write8NoThrow(CGA_CURSOR_LSB);
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
result = dataRegister.Write8NoThrow((byte)(cursor & 0xff));
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
}
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public bool SetCursorPosition(int column, int row)
|
|
{
|
|
if (column < 0 || column >= Columns ||
|
|
row < 0 || row >= Rows) {
|
|
return false;
|
|
}
|
|
cursor = column + row * Columns;
|
|
UpdateCursor(cursorVisible);
|
|
return true;
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public void ClearCursorToEndOfLine()
|
|
{
|
|
// Writing to the screen is slow. We might
|
|
// want to track how many characters are written per line
|
|
// to speed this up.
|
|
int column = cursor % Columns;
|
|
int row = cursor / Columns;
|
|
while (column != Columns) {
|
|
PutCharAt(' ', column++, row);
|
|
}
|
|
}
|
|
|
|
|
|
[NoHeapAllocation]
|
|
public bool PutCharAt(char c, int column, int row)
|
|
{
|
|
if (column < 0 || column >= Columns ||
|
|
row < 0 || row >= Rows) {
|
|
return false;
|
|
}
|
|
int offset = (column + row * Columns) * 2;
|
|
IoResult result = screenBuffer.Write16NoThrow(offset,
|
|
(ushort)(color | c));
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
return true;
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public void PutChar(char c)
|
|
{
|
|
PutChar((byte)c);
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public void PutChar(byte c)
|
|
{
|
|
if (screenBuffer == null) {
|
|
return;
|
|
}
|
|
|
|
if (c == (byte)'\b') {
|
|
cursor--;
|
|
}
|
|
else if (c == (byte)'\r') {
|
|
int left = Columns - (cursor % Columns);
|
|
|
|
cursor -= cursor % Columns;
|
|
}
|
|
else if (c == (byte)'\n') {
|
|
cursor += Columns - (cursor % Columns);
|
|
}
|
|
else {
|
|
IoResult result =
|
|
screenBuffer.Write16NoThrow(cursor++ * 2,
|
|
(ushort)(color | (c & 0x7f)));
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
}
|
|
|
|
if (cursor >= ScreenSize) {
|
|
IoResult result;
|
|
// Scroll up 1-line
|
|
result = screenBuffer.Copy8NoThrow(Columns * 2, 0,
|
|
(ScreenSize - Columns) * 2);
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
// Blank bottom line
|
|
result =
|
|
screenBuffer.Write16NoThrow((ScreenSize - Columns) * 2,
|
|
(ushort)(color | ' '),
|
|
Columns);
|
|
DebugStub.Assert(IoResult.Success == result);
|
|
// Reel in cursor
|
|
cursor -= Columns;
|
|
}
|
|
|
|
UpdateCursor(cursorVisible);
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
for (int i = 0; i < count; i++) {
|
|
PutChar(buffer[offset + i]);
|
|
}
|
|
}
|
|
|
|
private void WriteLine(String value)
|
|
{
|
|
for (int i = 0; i < value.Length; i++) {
|
|
PutChar((byte)value[i]);
|
|
}
|
|
PutChar((byte)'\r');
|
|
PutChar((byte)'\n');
|
|
}
|
|
}
|
|
} // namespace Microsoft.Singularity.Hal
|