diff --git a/src/Cosmos.Kernel.Native.x64/CPU/RhpStackProbe.asm b/src/Cosmos.Kernel.Native.x64/CPU/RhpStackProbe.asm new file mode 100644 index 00000000..11177fa8 --- /dev/null +++ b/src/Cosmos.Kernel.Native.x64/CPU/RhpStackProbe.asm @@ -0,0 +1,28 @@ +;; Stack probe helper +;; Probes each page from rsp down to r11 to ensure the guard page is touched +;; Input: +;; r11 - lowest address of the allocated stack frame ([InitialSp - FrameSize]) +;; rsp - some byte on the last probed page +;; Output: +;; rax - scratch (not preserved) +;; r11 - preserved +;; Notes: +;; Probes at least one page below rsp. +global RhpStackProbe +section .text + + +PROBE_STEP equ 0x1000 + +RhpStackProbe: + ; Align rax to the start of the current page + mov rax, rsp + and rax, -PROBE_STEP ; rax = lowest address on the last probed page + +ProbeLoop: + sub rax, PROBE_STEP ; move to the next page to probe + test dword [rax], eax ; touch the page + cmp rax, r11 + jg ProbeLoop ; continue if still above r11 + + ret diff --git a/src/Cosmos.Kernel.System.Graphics/Canvas.cs b/src/Cosmos.Kernel.System.Graphics/Canvas.cs index 69b849fe..03268e3d 100644 --- a/src/Cosmos.Kernel.System.Graphics/Canvas.cs +++ b/src/Cosmos.Kernel.System.Graphics/Canvas.cs @@ -10,6 +10,8 @@ public unsafe class Canvas public static uint Height; public static uint Pitch; + public static FontFormat DefaultFont = new BIOSFont(); + public static void DrawPixel(uint color, int x, int y) { if (x >= 0 && x < Width && y >= 0 && y < Height) @@ -83,16 +85,69 @@ public static void ClearScreen(uint color) public static void DrawChar(char c, int x, int y, uint color) { - PCScreenFont.PutChar(c, x, y, color, Color.Transparent); + DefaultFont.PutChar(c, x, y, color, Color.Transparent); } public static void DrawString(string text, int x, int y, uint color) { - PCScreenFont.PutString(text, x, y, color, Color.Transparent); + DefaultFont.PutString(text, x, y, color, Color.Transparent); } public static unsafe void DrawString(char* text, int x, int y, uint color) { - PCScreenFont.PutString(text, x, y, color, Color.Transparent); + DefaultFont.PutString(text, x, y, color, Color.Transparent); + } + + public static unsafe void DrawQuadraticBezier(uint color, int x0, int y0, int x1, int y1, int x2, int y2) + { + //Smooth line, so first calculate the distance between the start and end points to determine the number of steps + int dx = x2 - x0; + int dy = y2 - y0; + int distance = Math.Sqrt((int)(dx * dx + dy * dy)); + int steps = distance * 2; // More steps for smoother curve + for (int i = 0; i <= steps; i++) + { + float t = (float)i / steps; + float u = 1 - t; + int x = (int)(u * u * x0 + 2 * u * t * x1 + t * t * x2); + int y = (int)(u * u * y0 + 2 * u * t * y1 + t * t * y2); + DrawPixel(color, x, y); + } + } + + public static unsafe void DrawQuadraticBezierAA(uint color, int x0, int y0, int x1, int y1, int x2, int y2) + { + // First, calculate the distance between the start and end points to determine the number of steps, then each nearby pixel is drawn with an intensity based on its distance to the ideal curve + int dx = x2 - x0; + int dy = y2 - y0; + int distance = Math.Sqrt((int)(dx * dx + dy * dy)); + int steps = distance * 2; // More steps for smoother curve + for (int i = 0; i <= steps; i++) + { + float t = (float)i / steps; + float u = 1 - t; + int x = (int)(u * u * x0 + 2 * u * t * x1 + t * t * x2); + int y = (int)(u * u * y0 + 2 * u * t * y1 + t * t * y2); + + // Draw the pixel with anti-aliasing + for (int offsetX = -1; offsetX <= 1; offsetX++) + { + for (int offsetY = -1; offsetY <= 1; offsetY++) + { + int px = x + offsetX; + int py = y + offsetY; + if (px >= 0 && px < Width && py >= 0 && py < Height) + { + float distanceToCurve = Math.Sqrt(offsetX * offsetX + offsetY * offsetY); + if (distanceToCurve <= 1.5f) // Adjust the threshold for anti-aliasing effect + { + float intensity = 1.5f - distanceToCurve; // Closer pixels are more intense + uint blendedColor = Color.Blend(color, Address[py * (int)(Pitch / 4) + px], intensity); + DrawPixel(blendedColor, px, py); + } + } + } + } + } } } diff --git a/src/Cosmos.Kernel.System.Graphics/Color.cs b/src/Cosmos.Kernel.System.Graphics/Color.cs index 990361c5..494030c9 100644 --- a/src/Cosmos.Kernel.System.Graphics/Color.cs +++ b/src/Cosmos.Kernel.System.Graphics/Color.cs @@ -28,4 +28,24 @@ public class Color public static uint LightPink = 0xFFB6C1; public static uint LightBrown = 0xD2B48C; public static uint Transparent = 0x00000000; // Transparent color + + public static uint Blend(uint color1, uint color2, float ratio) + { + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; + + byte r1 = (byte)((color1 >> 16) & 0xFF); + byte g1 = (byte)((color1 >> 8) & 0xFF); + byte b1 = (byte)(color1 & 0xFF); + + byte r2 = (byte)((color2 >> 16) & 0xFF); + byte g2 = (byte)((color2 >> 8) & 0xFF); + byte b2 = (byte)(color2 & 0xFF); + + byte r = (byte)(r1 * (1 - ratio) + r2 * ratio); + byte g = (byte)(g1 * (1 - ratio) + g2 * ratio); + byte b = (byte)(b1 * (1 - ratio) + b2 * ratio); + + return (uint)((r << 16) | (g << 8) | b); + } } diff --git a/src/Cosmos.Kernel.System.Graphics/Fonts/BIOSFont.cs b/src/Cosmos.Kernel.System.Graphics/Fonts/BIOSFont.cs new file mode 100644 index 00000000..a658255e --- /dev/null +++ b/src/Cosmos.Kernel.System.Graphics/Fonts/BIOSFont.cs @@ -0,0 +1,109 @@ +using Cosmos.Kernel.Core.Memory; +using Cosmos.Kernel.System.Conversion; + +namespace Cosmos.Kernel.System.Graphics.Fonts; + +public unsafe class BIOSFont : FontFormat +{ + public static class Default + { + public static int Size = 4096; // Bytes array + + public static char* GetUnmanagedFontData() + { + // 8x16 BIOS font + string VGA816 = "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AAB4zMzM2MzGxsbMAAAAAAAA/sbGwMDAwMDAwAAAAAAAAAAA/mxsbGxsbGwAAAAAAAAA/sZgMBgwYMb+AAAAAAAAAAAAftjY2NjYcAAAAAAAAAAAZmZmZmZ8YGDAAAAAAAAAAHbcGBgYGBgYAAAAAAAAAH4YPGZmZjwYfgAAAAAAAAA4bMbG/sbGbDgAAAAAAAA4bMbGxmxsbGzuAAAAAAAAHjAYDD5mZmZmPAAAAAAAAAAAAH7b29t+AAAAAAAAAAAAAwZ+29vzfmDAAAAAAAAAHDBgYHxgYGAwHAAAAAAAAAB8xsbGxsbGxsYAAAAAAAAAAP4AAP4AAP4AAAAAAAAAAAAYGH4YGAAA/wAAAAAAAAAwGAwGDBgwAH4AAAAAAAAADBgwYDAYDAB+AAAAAAAADhsbGBgYGBgYGBgYGBgYGBgYGBgYGNjY2HAAAAAAAAAAABgYAH4AGBgAAAAAAAAAAAAAdtwAdtwAAAAAAAAAOGxsOAAAAAAAAAAAAAAAAAAAAAAAABgYAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAADwwMDAwM7GxsPBwAAAAAANhsbGxsbAAAAAAAAAAAAABw2DBgyPgAAAAAAAAAAAAAAAAAfHx8fHx8fAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; + fixed (char* ptr = VGA816) + { + return ptr; + } + } + } + + public static uint* Framebuffer; + private static bool _Initialized; + + private static uint Pitch; + private static byte* FontData; + + public override int CharWidth => 8; + + public override int CharHeight => 16; + + private static void EnsureInitialized() + { + if (!_Initialized) + { + byte* fontData = Base64.Decode(Default.GetUnmanagedFontData(), (uint)Default.Size); + Init(Graphics.Canvas.Address, Graphics.Canvas.Pitch, fontData); + _Initialized = true; + } + } + + public static void Init(uint* framebuffer, uint pitch, byte* fontData) + { + Framebuffer = framebuffer; + Pitch = pitch; + FontData = fontData; + } + + public override void PutChar(char c, int x, int y, uint fgcolor, uint bgcolor) + { + EnsureInitialized(); + + // Since this is a BIOS font, each bit represents a pixel (1 = on, 0 = off) + if (c < 0 || c > 127) + { + c = '?'; // Replace unsupported characters with '?' + } + + // If bgcolor is Transparent, we skip drawing background pixels + bool drawBackground = bgcolor != Color.Transparent; + byte* charData = FontData + (c * CharHeight); + for (int row = 0; row < CharHeight; row++) + { + byte rowData = charData[row]; + for (int col = 0; col < CharWidth; col++) + { + bool pixelOn = (rowData & (1 << (7 - col))) != 0; + int fbX = x + col; + int fbY = y + row; + if (fbX < 0 || fbX >= Graphics.Canvas.Width || fbY < 0 || fbY >= Graphics.Canvas.Height) + { + continue; // Skip pixels outside the framebuffer bounds + } + uint* pixelAddress = (uint*)((byte*)Framebuffer + (fbY * Pitch) + (fbX * 4)); + if (pixelOn) + { + *pixelAddress = fgcolor; + } + else if (drawBackground) + { + *pixelAddress = bgcolor; + } + } + } + } + + public override void PutString(string str, int x, int y, uint fgcolor, uint bgcolor) + { + EnsureInitialized(); + + for (int i = 0; i < str.Length; i++) + { + PutChar(str[i], x + (i * CharWidth), y, fgcolor, bgcolor); + } + } + + public override unsafe void PutString(char* str, int x, int y, uint fgcolor, uint bgcolor) + { + EnsureInitialized(); + + int i = 0; + while (str[i] != '\0') + { + PutChar(str[i], x + (i * CharWidth), y, fgcolor, bgcolor); + i++; + } + } +} \ No newline at end of file diff --git a/src/Cosmos.Kernel.System.Graphics/Fonts/FontFormat.cs b/src/Cosmos.Kernel.System.Graphics/Fonts/FontFormat.cs new file mode 100644 index 00000000..60740316 --- /dev/null +++ b/src/Cosmos.Kernel.System.Graphics/Fonts/FontFormat.cs @@ -0,0 +1,18 @@ +namespace Cosmos.Kernel.System.Graphics.Fonts; + +public abstract class FontFormat +{ + public abstract int CharWidth { get; } + public abstract int CharHeight { get; } + public abstract void PutChar(char c, int x, int y, uint fgcolor, uint bgcolor); + public abstract void PutString(string str, int x, int y, uint fgcolor, uint bgcolor); + public abstract unsafe void PutString(char* str, int x, int y, uint fgcolor, uint bgcolor); + public virtual void PutScaledChar(char c, int x, int y, uint fgcolor, uint bgcolor, int scale) {} + public virtual void PutScaledString(string str, int x, int y, uint fgcolor, uint bgcolor, int scale) + { + for (int i = 0; i < str.Length; i++) + { + PutScaledChar(str[i], x, y, fgcolor, bgcolor, scale); + } + } +} \ No newline at end of file diff --git a/src/Cosmos.Kernel.System.Graphics/KernelConsole.cs b/src/Cosmos.Kernel.System.Graphics/KernelConsole.cs index a296e0b2..fe80f300 100644 --- a/src/Cosmos.Kernel.System.Graphics/KernelConsole.cs +++ b/src/Cosmos.Kernel.System.Graphics/KernelConsole.cs @@ -10,8 +10,8 @@ public static class KernelConsole { private static int _cursorX; private static int _cursorY; - private static int CharWidth => PCScreenFont.CharWidth; - private static int CharHeight => PCScreenFont.CharHeight; + private static int CharWidth => Canvas.DefaultFont.CharWidth; + private static int CharHeight => Canvas.DefaultFont.CharHeight; private const int LineSpacing = 0; private static bool _isInitialized = false; diff --git a/src/Cosmos.Kernel.System/Math.cs b/src/Cosmos.Kernel.System/Math.cs index b2205a0f..b60459e5 100644 --- a/src/Cosmos.Kernel.System/Math.cs +++ b/src/Cosmos.Kernel.System/Math.cs @@ -16,4 +16,36 @@ public static int Max(int a, int b) { return a > b ? a : b; } + + public static int Clamp(int value, int min, int max) + { + if (value < min) return min; + if (value > max) return max; + return value; + } + + public static int Pow(int x, int y) + { + if (y < 0) throw new ArgumentOutOfRangeException(nameof(y), "Exponent must be non-negative."); + int result = 1; + for (int i = 0; i < y; i++) + { + result *= x; + } + return result; + } + + public static int Sqrt(int value) + { + if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Cannot compute square root of a negative number."); + if (value == 0) return 0; + int x = value; + int y = (x + 1) / 2; + while (y < x) + { + x = y; + y = (value / x + x) / 2; + } + return x; + } }