/
TextScreen.cs
222 lines (203 loc) · 7.87 KB
/
TextScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cosmos.Core;
using Cosmos.Debug.Kernel;
namespace Cosmos.HAL
{
// Dont hold state here. This is a raw to hardware class. Virtual screens should be done
// by memory moves
public class TextScreen : TextScreenBase
{
protected byte Color = 0x0F; // White
protected ushort mBackgroundClearCellValue;
protected ushort mTextClearCellValue;
public uint mRow2Addr;
public uint mScrollSize;
protected int mCursorSize = 25; // 25 % as C# Console class
protected bool mCursorVisible = true;
public MemoryBlock08 mRAM;
/// <summary>
/// Memory.
/// </summary>
public MemoryBlock Memory = new MemoryBlock(0xB8000, 80 * 25 * 2);
// These should probably move to a VGA class later, or this class should be remade into a VGA class
/// <summary>
/// Misc. output.
/// </summary>
public const int MiscOutput = 0x03C2;
/// <summary>
/// First IOPort index.
/// </summary>
public const int Idx1 = 0x03C4;
/// <summary>
/// First IOPort data.
/// </summary>
public const int Data1 = 0x03C5;
/// <summary>
/// Second IOPort index.
/// </summary>
public const int Idx2 = 0x03CE;
/// <summary>
/// Second IOPort data.
/// </summary>
public const int Data2 = 0x03CF;
/// <summary>
/// Third IOPort index.
/// </summary>
public const int Idx3 = 0x03D4;
/// <summary>
/// Third IOPort data.
/// </summary>
public const int Data3 = 0x03D5;
/// <summary>
/// Creat new instance of the <see cref="TextScreen"/> class.
/// </summary>
public TextScreen()
{
mRAM = Memory.Bytes;
// Set the Console default colors: White foreground on Black background, the default value of mClearCellValue is set there too as it is linked with the Color
SetColors(ConsoleColor.White, ConsoleColor.Black);
mBackgroundClearCellValue = mTextClearCellValue;
mRow2Addr = (uint)(Cols * 2);
mScrollSize = (uint)(Cols * (Rows - 1) * 2);
SetCursorSize(mCursorSize);
SetCursorVisible(mCursorVisible);
TextScreenHelpers.Debug("End of TextScreen..ctor");
}
public void UpdateWindowSize()
{
Memory = new Cosmos.Core.MemoryBlock(0xB8000, (uint)(Cols * Rows * 2));
mRAM = Memory.Bytes;
mScrollSize = (uint)(Cols * (Rows - 1) * 2);
mRow2Addr = (uint)(Cols * 2);
}
public override ushort Rows { set; get; } = 25;
public override ushort Cols { set; get; } = 80;
/// <summary>
/// Clear text screen.
/// </summary>
public override void Clear()
{
TextScreenHelpers.Debug("Clearing screen with value ");
TextScreenHelpers.DebugNumber(mTextClearCellValue);
Memory.Fill(mTextClearCellValue);
mBackgroundClearCellValue = mTextClearCellValue;
}
/// <summary>
/// Scroll screen up.
/// </summary>
public override void ScrollUp()
{
Memory.MoveDown(0, mRow2Addr, mScrollSize);
Memory.Fill(mScrollSize, mRow2Addr, mBackgroundClearCellValue);
}
public override byte this[int aX, int aY]
{
get
{
var xScreenOffset = (uint)((aX + aY * Cols) * 2);
return (byte)mRAM[xScreenOffset];
}
set
{
var xScreenOffset = (uint)((aX + aY * Cols) * 2);
mRAM[xScreenOffset] = value;
mRAM[xScreenOffset + 1] = Color;
}
}
/// <summary>
/// Set screen foreground and background colors.
/// </summary>
/// <param name="aForeground">Foreground color.</param>
/// <param name="aBackground">Background color.</param>
public override void SetColors(ConsoleColor aForeground, ConsoleColor aBackground)
{
//Color = (byte)((byte)(aForeground) | ((byte)(aBackground) << 4));
// TODO: Use Real Mode to clear in Mode Control Register Index 10
// the third bit to disable blinking and use the seventh bit
// as the bright bit on background color for brighter colors :)
Color = (byte)(((byte)aForeground | ((byte)aBackground << 4)) & 0x7F);
// The Color | the NUL character this is used to Clear the Screen
mTextClearCellValue = (ushort)(Color << 8 | 0x00);
}
/// <summary>
/// Set cursor position.
/// </summary>
/// <param name="aX">A position on X axis.</param>
/// <param name="aY">A position on Y axis.</param>
public override void SetCursorPos(int aX, int aY)
{
char xPos = (char)(aY * Cols + aX);
// Cursor low byte to VGA index register
IOPort.Write8(Idx3, 0x0F);
IOPort.Write8(Data3, (byte)(xPos & 0xFF));
// Cursor high byte to VGA index register
IOPort.Write8(Idx3, 0x0E);
IOPort.Write8(Data3, (byte)(xPos >> 8));
}
/// <summary>
/// Get screen color.
/// </summary>
/// <returns>byte value.</returns>
public override byte GetColor()
{
return Color;
}
/// <summary>
/// Get cursor size.
/// </summary>
/// <returns>int value.</returns>
public override int GetCursorSize()
{
return mCursorSize;
}
/// <summary>
/// Set cursor size.
/// </summary>
/// <param name="value">Size value.</param>
public override void SetCursorSize(int value)
{
mCursorSize = value;
TextScreenHelpers.Debug("Changing cursor size to", value, "%");
// We need to transform value from a percentage to a value from 15 to 0
value = 16 - 16 * value / 100;
// This is the case in which value is in reality 1% and a for a truncation error we get 16 (invalid value)
if (value >= 16)
{
value = 15;
}
TextScreenHelpers.Debug("verticalSize is", value);
// Cursor Vertical Size Register here a value between 0x00 and 0x0F must be set with 0x00 meaning maximum size and 0x0F minimum
IOPort.Write8(Idx3, 0x0A);
IOPort.Write8(Data3, (byte)value);
// Cursor Horizontal Size Register we set it to 0x0F (100%) as a security measure is probably so already
IOPort.Write8(Idx3, 0x0B);
IOPort.Write8(Data3, 0x0F);
}
/// <summary>
/// Check if cursor is visible.
/// </summary>
/// <returns>bool value.</returns>
public override bool GetCursorVisible()
{
return mCursorVisible;
}
/// <summary>
/// Set cursor visibilty.
/// </summary>
/// <param name="value">TRUE - visible.</param>
public override void SetCursorVisible(bool value)
{
mCursorVisible = value;
// The VGA Cursor is disabled when the value is 1 and enabled when is 0 so we need to invert 'value', sadly the ConvertToByte() function is not working
// so we need to do the if by hand...
byte cursorDisable = (byte)(mCursorVisible ? 0 : 1);
// Cursor Vertical Size Register if the bit 5 is set to 1 the cursor is disabled, if 0 is enabled
IOPort.Write8(Idx3, 0x0A);
IOPort.Write8(Data3, (byte)(IOPort.Read8(Data3) | (byte)(cursorDisable << 5)));
SetCursorSize(mCursorSize);
}
}
}