Skip to content
This repository has been archived by the owner on Jun 24, 2024. It is now read-only.

Commit

Permalink
Console.Print, PrintLine and Clear methods (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
FrankRay78 authored Apr 12, 2024
1 parent 96ea751 commit f878abf
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 42 deletions.
254 changes: 228 additions & 26 deletions src/PatienceOS.Kernel.Tests/ConsoleTests.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,228 @@
using Xunit;

namespace PatienceOS.Kernel.Tests
{
unsafe public class ConsoleTests
{
[Fact]
public void Console_Should_Write_Hello()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.Print("Hello");

// Then
Assert.Equal((byte)'H', frameBuffer.Fetch(0));
Assert.Equal((byte)'e', frameBuffer.Fetch(2));
Assert.Equal((byte)'l', frameBuffer.Fetch(4));
Assert.Equal((byte)'l', frameBuffer.Fetch(6));
Assert.Equal((byte)'o', frameBuffer.Fetch(8));
}
}
}
using Xunit;

namespace PatienceOS.Kernel.Tests
{
unsafe public class ConsoleTests
{
[Fact]
public void Should_Write_AB()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.Print("AB");

// Then
Assert.Equal((byte)'A', buffer[0]);
Assert.Equal((byte)'B', buffer[2]);
}

[Fact]
public void Should_Clear_AB()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.Print("AB");
console.Clear();

// Then
Assert.Equal((byte)' ', buffer[0]);
Assert.Equal((byte)' ', buffer[2]);
}

[Fact]
public void Should_Write_A_EOL_B()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.Print("A\nB");

// Then
Assert.Equal((byte)'A', buffer[0]);
Assert.Equal((byte)'B', buffer[80 * 2 + 0]);
}

[Fact]
public void Should_Clear_A_EOL_B()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.Print("A\nB");
console.Clear();

// Then
Assert.Equal((byte)' ', buffer[0]);
Assert.Equal((byte)' ', buffer[80 * 2 + 0]);
}

unsafe public class ThreeXThree
{
[Fact]
public void Should_Write_ABC_EOL_DEF_EOL_GHI()
{
// Given
byte* buffer = stackalloc byte[3 * 3 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(3, 3, frameBuffer);

// When
console.Print("ABCDEFGHI");

// Then
Assert.Equal((byte)'A', buffer[0]);
Assert.Equal((byte)'B', buffer[2]);
Assert.Equal((byte)'C', buffer[4]);
Assert.Equal((byte)'D', buffer[6]);
Assert.Equal((byte)'E', buffer[8]);
Assert.Equal((byte)'F', buffer[10]);
Assert.Equal((byte)'G', buffer[12]);
Assert.Equal((byte)'H', buffer[14]);
Assert.Equal((byte)'I', buffer[16]);
}
}

unsafe public class HelloWorld
{
[Fact]
public void Should_Write_Hello_Space_World_With_Print_Statement()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.Print("Hello World");

// Then
Assert.Equal((byte)'H', buffer[0]);
Assert.Equal((byte)'e', buffer[2]);
Assert.Equal((byte)'l', buffer[4]);
Assert.Equal((byte)'l', buffer[6]);
Assert.Equal((byte)'o', buffer[8]);
Assert.Equal((byte)' ', buffer[10]);
Assert.Equal((byte)'W', buffer[12]);
Assert.Equal((byte)'o', buffer[14]);
Assert.Equal((byte)'r', buffer[16]);
Assert.Equal((byte)'l', buffer[18]);
Assert.Equal((byte)'d', buffer[20]);
}

[Fact]
public void Should_Write_Hello_Space_World_With_Print_Statements()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.Print("Hello");
console.Print(" ");
console.Print("World");

// Then
Assert.Equal((byte)'H', buffer[0]);
Assert.Equal((byte)'e', buffer[2]);
Assert.Equal((byte)'l', buffer[4]);
Assert.Equal((byte)'l', buffer[6]);
Assert.Equal((byte)'o', buffer[8]);
Assert.Equal((byte)' ', buffer[10]);
Assert.Equal((byte)'W', buffer[12]);
Assert.Equal((byte)'o', buffer[14]);
Assert.Equal((byte)'r', buffer[16]);
Assert.Equal((byte)'l', buffer[18]);
Assert.Equal((byte)'d', buffer[20]);
}

[Fact]
public void Should_Write_Hello_EOL_World_EOL_With_Print_Statement()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.Print("Hello\nWorld\n");

// Then
Assert.Equal((byte)'H', buffer[0]);
Assert.Equal((byte)'e', buffer[2]);
Assert.Equal((byte)'l', buffer[4]);
Assert.Equal((byte)'l', buffer[6]);
Assert.Equal((byte)'o', buffer[8]);
Assert.Equal((byte)'W', buffer[80 * 2 + 0]);
Assert.Equal((byte)'o', buffer[80 * 2 + 2]);
Assert.Equal((byte)'r', buffer[80 * 2 + 4]);
Assert.Equal((byte)'l', buffer[80 * 2 + 6]);
Assert.Equal((byte)'d', buffer[80 * 2 + 8]);
}

[Fact]
public void Should_Write_Hello_EOL_World_EOL_With_Print_Statements()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.Print("Hello\n");
console.Print("World\n");

// Then
Assert.Equal((byte)'H', buffer[0]);
Assert.Equal((byte)'e', buffer[2]);
Assert.Equal((byte)'l', buffer[4]);
Assert.Equal((byte)'l', buffer[6]);
Assert.Equal((byte)'o', buffer[8]);
Assert.Equal((byte)'W', buffer[80 * 2 + 0]);
Assert.Equal((byte)'o', buffer[80 * 2 + 2]);
Assert.Equal((byte)'r', buffer[80 * 2 + 4]);
Assert.Equal((byte)'l', buffer[80 * 2 + 6]);
Assert.Equal((byte)'d', buffer[80 * 2 + 8]);
}

[Fact]
public void Should_Write_Hello_EOL_World_EOL_With_PrintLine_Statements()
{
// Given
byte* buffer = stackalloc byte[80 * 25 * 2];
var frameBuffer = new FrameBuffer(buffer);
var console = new Console(80, 25, frameBuffer);

// When
console.PrintLine("Hello");
console.PrintLine("World");

// Then
Assert.Equal((byte)'H', buffer[0]);
Assert.Equal((byte)'e', buffer[2]);
Assert.Equal((byte)'l', buffer[4]);
Assert.Equal((byte)'l', buffer[6]);
Assert.Equal((byte)'o', buffer[8]);
Assert.Equal((byte)'W', buffer[80 * 2 + 0]);
Assert.Equal((byte)'o', buffer[80 * 2 + 2]);
Assert.Equal((byte)'r', buffer[80 * 2 + 4]);
Assert.Equal((byte)'l', buffer[80 * 2 + 6]);
Assert.Equal((byte)'d', buffer[80 * 2 + 8]);
}
}
}
}
10 changes: 5 additions & 5 deletions src/PatienceOS.Kernel.Tests/FrameBufferTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ public void FrameBuffer_Should_Contain_Hello()
frameBuffer.Write(4, (byte)'o');

// Then
Assert.Equal((byte)'H', frameBuffer.Fetch(0));
Assert.Equal((byte)'e', frameBuffer.Fetch(1));
Assert.Equal((byte)'l', frameBuffer.Fetch(2));
Assert.Equal((byte)'l', frameBuffer.Fetch(3));
Assert.Equal((byte)'o', frameBuffer.Fetch(4));
Assert.Equal((byte)'H', buffer[0]);
Assert.Equal((byte)'e', buffer[1]);
Assert.Equal((byte)'l', buffer[2]);
Assert.Equal((byte)'l', buffer[3]);
Assert.Equal((byte)'o', buffer[4]);
}
}
}
53 changes: 47 additions & 6 deletions src/PatienceOS.Kernel/Console.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ unsafe public struct Console

private FrameBuffer frameBuffer;

private int pos = 0;
private int column = 0; // nb. Incremented by two for each write, to account for the text colouring
private int row = 0;

private byte foregroundColor = 0x0F; // White

public Console(int width, int height, FrameBuffer frameBuffer)
Expand All @@ -25,27 +27,66 @@ public Console(int width, int height, FrameBuffer frameBuffer)
/// </summary>
public void Clear()
{
for (int i = 0; i < width * height * 2; i++)
for (int i = 0; i < width * height * 2; i += 2)
{
frameBuffer.Write(i, 0);
// Write directly to the video memory
frameBuffer.Write(i, (byte)' ');
frameBuffer.Write(i + 1, foregroundColor);
}

// Reset the cursor position
column = 0;
row = 0;
}

/// <summary>
/// Print a string to the current cursor position
/// </summary>
/// <remarks>
/// Assumes each screen character is represented by two bytes aligned as a 16-bit word,
/// see <see cref="https://en.wikipedia.org/wiki/VGA_text_mode#Data_arrangement"/>
/// </remarks>
public void Print(string s)
{
fixed (char* ps = s)
{
for (int i = 0; i < s.Length; i++)
{
frameBuffer.Write(pos, (byte)ps[i]);
frameBuffer.Write(pos + 1, foregroundColor);
// Perform a CRLF if we encounter a Newline character
if (ps[i] == '\n')
{
column = 0;
row++;

continue;
}

// Perform a CRLF when the cursor reaches the end of the terminal line
// eg.column is 0 to 79, width = 80
if (column >= width * 2)
{
column = 0;
row++;
}

// Write directly to the video memory
frameBuffer.Write(row * width * 2 + column, (byte)ps[i]);
frameBuffer.Write(row * width * 2 + column + 1, foregroundColor);

pos += 2;
// Move the cursor right by one
column += 2;
}
}
}

/// <summary>
/// Prints a string to the current cursor position
/// and then moves the cursor to the next line
/// </summary>
public void PrintLine(string s)
{
Print(s);
Print("\n");
}
}
}
5 changes: 0 additions & 5 deletions src/PatienceOS.Kernel/FrameBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ public FrameBuffer(byte* buffer)
this.buffer = buffer;
}

public byte Fetch(int position)
{
return buffer[position];
}

public void Write(int position, byte value)
{
buffer[position] = value;
Expand Down

0 comments on commit f878abf

Please sign in to comment.