├── Resources ├── diskformat ├── amethyst.png ├── screenshot.png ├── Images │ └── amethyst.tga ├── amethyst-logotext.png ├── Fonts │ └── Tamsyn │ │ ├── Tamsyn5x9b.psf │ │ ├── Tamsyn5x9r.psf │ │ ├── Tamsyn6x12b.psf │ │ ├── Tamsyn6x12r.psf │ │ ├── Tamsyn7x13b.psf │ │ ├── Tamsyn7x13r.psf │ │ ├── Tamsyn7x14b.psf │ │ ├── Tamsyn7x14r.psf │ │ ├── Tamsyn8x15b.psf │ │ ├── Tamsyn8x15r.psf │ │ ├── Tamsyn8x16b.psf │ │ ├── Tamsyn8x16r.psf │ │ ├── Tamsyn10x20b.psf │ │ ├── Tamsyn10x20r.psf │ │ ├── LICENSE │ │ └── README ├── grub.cfg └── Linker-Script.ld ├── .gitattributes ├── .gitignore ├── Source ├── Includes │ ├── Time │ │ └── Instant.hpp │ ├── memory.h │ ├── Graphics │ │ ├── TargaImage.hpp │ │ ├── Window.hpp │ │ ├── TextLabel.hpp │ │ ├── MouseMoveEvent.hpp │ │ ├── Desktop.hpp │ │ ├── TextConsole.hpp │ │ ├── canvas.h │ │ ├── screenfont.h │ │ └── Widget.hpp │ ├── cpuid.hpp │ ├── Drivers │ │ ├── keyboard.h │ │ ├── ps2controller.h │ │ ├── piixide.h │ │ ├── mouse.h │ │ ├── qemuVga.h │ │ ├── serial.h │ │ ├── pciBus.h │ │ └── atiRage128.h │ ├── Device.hpp │ ├── Registers.h │ ├── Types.h │ ├── amethyst.h │ ├── shell.hpp │ ├── Structures │ │ ├── fifobuffer.h │ │ └── linkedlist.hpp │ ├── string.h │ ├── cppsupport.hpp │ ├── debug.h │ ├── StandardIO.hpp │ ├── physicalMemory.h │ ├── stream.h │ ├── portIO.h │ ├── memoryManager.h │ ├── GDT.h │ ├── deviceTree.h │ ├── thread.h │ ├── Clock.h │ ├── multiboot.h │ └── interrupts.h ├── drivers │ ├── pci │ │ ├── deviceNames.h │ │ └── deviceNames.c │ ├── mouse.c │ ├── keyboard.c │ ├── qemu_display_driver.c │ ├── serial.c │ ├── atiRage128.c │ ├── piixide.c │ └── ps2controller.c ├── library │ ├── memory.c │ ├── string.c │ └── fifobuffer.c ├── StandardIO.cpp ├── MouseMoveEvent.cpp ├── arch │ └── x86_32 │ │ ├── paging.h │ │ ├── gdt.S │ │ ├── portIO.c │ │ ├── thread.c │ │ ├── taskswitch.asm │ │ ├── physicalMemory.c │ │ ├── entry.S │ │ ├── interrupts_handler.c │ │ ├── rootDevice.c │ │ └── interrupts_stubs.S ├── GUI │ ├── TextLabel.cpp │ ├── Window.cpp │ ├── Widget.cpp │ ├── Desktop.cpp │ ├── TargaImage.cpp │ └── TextConsole.cpp ├── graphics.c ├── debug.c ├── cpuid.cpp ├── deviceTree.c ├── stream.c └── shell.cpp ├── Docs ├── Testing.md ├── Streams.md ├── Architecture.md ├── Platforms.md ├── devices-and-drivers.md └── Memory Management.md ├── LICENSE.md ├── README.md └── Makefile /Resources/diskformat: -------------------------------------------------------------------------------- 1 | 2048,,0x0C,* -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.c linguist-language=C 2 | *.h linguist-language=C 3 | 4 | -------------------------------------------------------------------------------- /Resources/amethyst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/amethyst.png -------------------------------------------------------------------------------- /Resources/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/screenshot.png -------------------------------------------------------------------------------- /Resources/Images/amethyst.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Images/amethyst.tga -------------------------------------------------------------------------------- /Resources/amethyst-logotext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/amethyst-logotext.png -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn5x9b.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn5x9b.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn5x9r.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn5x9r.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn6x12b.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn6x12b.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn6x12r.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn6x12r.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn7x13b.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn7x13b.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn7x13r.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn7x13r.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn7x14b.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn7x14b.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn7x14r.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn7x14r.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn8x15b.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn8x15b.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn8x15r.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn8x15r.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn8x16b.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn8x16b.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn8x16r.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn8x16r.psf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | /Synergy-OS.iso 3 | /Amethyst.iso 4 | /dis*.img 5 | /kernel32 6 | /Build/ 7 | *.o 8 | /.vs 9 | .vscode/ -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn10x20b.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn10x20b.psf -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/Tamsyn10x20r.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackScottAU/Amethyst/HEAD/Resources/Fonts/Tamsyn/Tamsyn10x20r.psf -------------------------------------------------------------------------------- /Source/Includes/Time/Instant.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Time { 4 | 5 | /// @brief Represents an instant of time. Number of ticks (65536ths of a second) since 2000-01-01 in UTC. 6 | class Instant { 7 | public: 8 | Instant(); 9 | 10 | private: 11 | sint64 ticks; 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /Docs/Testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | ## Virtual Platforms 4 | 5 | - QEMU (440FX/PIIX3) 6 | - VirtualBox (440FX/PIIX3) 7 | - Hyper-V (440BX/PIIX4) 8 | 9 | ## Real Platforms 10 | 11 | - Jack's Test x86_32 PC - "Gresley" (440LX/PIIX4) 12 | 13 | 14 | ## Cards and Devices 15 | 16 | Noteworthy is the ATI Rage 128 series is supported both as real hardware and emulated in QEMU. Only the Cirrus Logic card also does this and has better than VGA. 17 | -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/LICENSE: -------------------------------------------------------------------------------- 1 | Tamsyn font is free. You are hereby granted permission to use, copy, modify, 2 | and distribute it as you see fit. 3 | 4 | Tamsyn font is provided "as is" without any express or implied warranty. 5 | 6 | The author makes no representations about the suitability of this font for 7 | a particular purpose. 8 | 9 | In no event will the author be held liable for damages arising from the use 10 | of this font. 11 | -------------------------------------------------------------------------------- /Source/drivers/pci/deviceNames.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - List of PCI device names. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef DRIVERS_PCI_DEVICENAMES_H_ 8 | #define DRIVERS_PCI_DEVICENAMES_H_ 9 | 10 | #include 11 | 12 | 13 | 14 | char* pci_getNameFromVendorAndDevice(uint16 vendor, uint16 device); 15 | 16 | #endif // DRIVERS_PCI_DEVICENAMES_H_ 17 | -------------------------------------------------------------------------------- /Source/Includes/memory.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Memory 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_MEMORY_H_ 8 | #define INCLUDES_MEMORY_H_ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | 15 | void* memset(void* b, int c, int len); 16 | void memzero(void* b, int len) ; 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | 22 | #endif // INCLUDES_MEMORY_H_ 23 | -------------------------------------------------------------------------------- /Source/Includes/Graphics/TargaImage.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * A very simple image widget that loads data from a .tga image file and draws it on the canvas. 6 | */ 7 | class TargaImage : public Widget { 8 | public: 9 | TargaImage(uint8* ptr, uint32 length, uint32 x, uint32 y, Canvas* canvas); 10 | 11 | void Redraw(); 12 | void HandleUIEvent(GuiEvent* eventData); 13 | 14 | using Widget::SetPosition; 15 | 16 | private: 17 | uint32* pixels; 18 | }; 19 | -------------------------------------------------------------------------------- /Source/library/memory.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Memory manipulation functions. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | 9 | void* memset(void *b, int c, int len) { 10 | unsigned char *p = (unsigned char*)b; 11 | 12 | while (len > 0) { 13 | *p = c; 14 | p++; 15 | len--; 16 | } 17 | return(b); 18 | } 19 | 20 | void memzero(void* b, int len) { 21 | memset(b, 0, len); 22 | } 23 | -------------------------------------------------------------------------------- /Source/StandardIO.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | StandardIO::StandardIO(void (*stdout)(char), char (*stdin)(void)) { 5 | this->stdin = stdin; 6 | this->stdout = stdout; 7 | } 8 | 9 | void StandardIO::Print(const char* formatString, ...) { 10 | va_list args; 11 | va_start(args, formatString); 12 | 13 | stream_vprintf(this->stdout, formatString, args); 14 | 15 | va_end(args); 16 | } 17 | 18 | char* StandardIO::ReadLine(bool echo) { 19 | return stream_readLine(this->stdin, this->stdout, echo); 20 | } 21 | -------------------------------------------------------------------------------- /Source/Includes/cpuid.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - CPUID stuff. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | 9 | #ifndef INCLUDES_CPUID_HPP_ 10 | #define INCLUDES_CPUID_HPP_ 11 | 12 | class CPUID { 13 | public: 14 | CPUID(); 15 | 16 | uint8* getManufacturerString(); 17 | 18 | uint8 getFamily(); 19 | uint8 getModel(); 20 | uint8 getStepping(); 21 | 22 | private: 23 | uint32 maxLevel; 24 | }; 25 | 26 | #endif // INCLUDES_CPUID_HPP_ 27 | -------------------------------------------------------------------------------- /Source/Includes/Drivers/keyboard.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - PS/2 Keyboard driver. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_KEYBOARD_H_ 8 | #define INCLUDES_KEYBOARD_H_ 9 | 10 | #include 11 | #include 12 | 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | deviceTree_Entry* keyboard_initialise(void); 19 | char keyboard_readChar(void); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif // INCLUDES_KEYBOARD_H_ 26 | -------------------------------------------------------------------------------- /Source/MouseMoveEvent.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | MouseMoveEvent::MouseMoveEvent(sint8 moveX, sint8 moveY) 4 | { 5 | this->mouseX = moveX; 6 | this->mouseY = moveY; 7 | } 8 | 9 | sint8 MouseMoveEvent::getMouseX() { 10 | return this->mouseX; 11 | } 12 | 13 | sint8 MouseMoveEvent::getMouseY() { 14 | return this->mouseY; 15 | } 16 | 17 | GuiEventType MouseMoveEvent::GetEventType() 18 | { 19 | return GuiEventType::MOUSE_MOVE; 20 | } 21 | 22 | GuiEventType MouseClickEvent::GetEventType() 23 | { 24 | return GuiEventType::MOUSE_CLICK; 25 | } 26 | -------------------------------------------------------------------------------- /Source/Includes/Graphics/Window.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /** 7 | * Like a program. 8 | */ 9 | class Window : public Widget { 10 | public: 11 | Window(ScreenFont* font, uint32 x, uint32 y, uint32 w, uint32 h, Canvas* canvas, const char* name); 12 | 13 | void Redraw(); 14 | void HandleUIEvent(GuiEvent* eventData); 15 | 16 | void SetName(char* text); 17 | 18 | using Widget::SetPosition; 19 | 20 | private: 21 | ScreenFont* font; 22 | const char* text; 23 | }; 24 | -------------------------------------------------------------------------------- /Source/Includes/Device.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifndef INCLUDES_DEVICE_HPP_ 6 | #define INCLUDES_DEVICE_HPP_ 7 | 8 | /** 9 | * An abstract class representing a hardware device. Calling initialise will set up the device (and any children managed by this device). 10 | */ 11 | class Device { 12 | public: 13 | virtual void Initialise() = 0; 14 | virtual void PrintDeviceDetails(StandardIO* stdio, bool verbose, uint16 depth) = 0; 15 | virtual LinkedList* GetChildren() = 0; 16 | }; 17 | 18 | #endif // INCLUDES_DEVICE_HPP_ 19 | -------------------------------------------------------------------------------- /Source/Includes/Graphics/TextLabel.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /** 7 | * Uses a screenfont to display text. 8 | */ 9 | class TextLabel : public Widget { 10 | public: 11 | TextLabel(ScreenFont* font, uint32 x, uint32 y, Canvas* canvas, char* text, uint32 colour); 12 | 13 | void Redraw(); 14 | void HandleUIEvent(GuiEvent* eventData); 15 | 16 | void SetFont(ScreenFont* font); 17 | void SetText(char* text); 18 | 19 | using Widget::SetPosition; 20 | 21 | private: 22 | ScreenFont* font; 23 | char* text; 24 | uint32 colour; 25 | }; 26 | -------------------------------------------------------------------------------- /Resources/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=1 2 | set default=0 # Set the default menu entry. 3 | 4 | menuentry "Amethyst" { 5 | # Load the Amethyst kernel via Multiboot v1. 6 | multiboot /kernel32 7 | 8 | # Files required by the kernel before the root file system is available are loaded below. 9 | # Names are listed twice - the first is the actual file name, the second is the path name given to the kernel through Multiboot. 10 | module /Fonts/Tamsyn/Tamsyn8x16r.psf /Fonts/Tamsyn/Tamsyn8x16r.psf 11 | module /Images/amethyst.tga /Images/amethyst.tga 12 | module /Fonts/Tamsyn/Tamsyn8x16b.psf /Fonts/Tamsyn/Tamsyn8x16b.psf 13 | } 14 | -------------------------------------------------------------------------------- /Source/Includes/Drivers/ps2controller.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Driver for 8042 PS/2 Keyboard and Mouse Controller. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_PS2CONTROLLER_H_ 8 | #define INCLUDES_PS2CONTROLLER_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | deviceTree_Entry* ps2controller_initialise(void); 17 | 18 | void ps2controller_sendByteToDevice(uint8 channel, uint8 data); 19 | uint8 ps2controller_receiveByteFromDevice(uint8 channel); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif // INCLUDES_PS2CONTROLLER_H_ 26 | -------------------------------------------------------------------------------- /Source/Includes/Drivers/piixide.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Driver for PCI IDE Controllers. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_PIIXIDE_H_ 8 | #define INCLUDES_PIIXIDE_H_ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | /** 19 | * Only thing this does is list resources. This is actually a generic PCI IDE controller, not PIIX specific. 20 | */ 21 | deviceTree_Entry* piixide_initialise(pciBus_Entry* device); 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | #endif // INCLUDES_PIIXIDE_H_ 28 | -------------------------------------------------------------------------------- /Source/Includes/Drivers/mouse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - PS/2 Keyboard driver. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_MOUSE_H_ 8 | #define INCLUDES_MOUSE_H_ 9 | 10 | #include 11 | #include 12 | 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | deviceTree_Entry* mouse_initialise(void); 19 | 20 | // When we convert mouse to be a C++ file we can do something like mouse_registerRootWidget() instead. 21 | void sortOfMouse_HandleEvent(sint16 moveX, sint16 moveY); 22 | void sortOfMouse_HandleClickEvent(); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | 28 | #endif // INCLUDES_MOUSE_H_ 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2024, Jack Scott 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /Source/Includes/Registers.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - x86 interrupt handler register layout. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_REGISTERS_H_ 8 | #define INCLUDES_REGISTERS_H_ 9 | 10 | /// @brief Form of the registers passed to the interrupt handler by the CPU. 11 | struct Registers_S { 12 | unsigned int GS, FS, ES, DS; // pushed last. 13 | unsigned int EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX; // pusha 14 | unsigned int IntNum, ErrorCode; // pushed by the stub (and sometimes the error code pushed by the interrupt) 15 | unsigned int EIP, CS, EFlags, UserESP, SS; // pushed by the interrupt itself. 16 | }; 17 | 18 | #endif // INCLUDES_REGISTERS_H_ 19 | -------------------------------------------------------------------------------- /Source/Includes/Graphics/MouseMoveEvent.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef MOUSEMOVEEVNT 7 | #define MOUSEMOVEEVNT 8 | 9 | enum GuiEventType { 10 | MOUSE_CLICK, 11 | 12 | MOUSE_MOVE, 13 | }; 14 | 15 | class GuiEvent { 16 | public: 17 | virtual GuiEventType GetEventType() = 0; 18 | }; 19 | 20 | class MouseClickEvent : public GuiEvent { 21 | public: 22 | GuiEventType GetEventType(); 23 | }; 24 | 25 | class MouseMoveEvent : public GuiEvent { 26 | public: 27 | MouseMoveEvent(sint8 moveX, sint8 moveY); 28 | 29 | sint8 getMouseX(); 30 | sint8 getMouseY(); 31 | 32 | GuiEventType GetEventType(); 33 | 34 | 35 | private: 36 | uint16 mouseX; 37 | uint16 mouseY; 38 | }; 39 | 40 | #endif -------------------------------------------------------------------------------- /Source/Includes/Types.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Standard types 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_TYPES_H_ 8 | #define INCLUDES_TYPES_H_ 9 | 10 | #include 11 | #include 12 | 13 | // Basic Type Definitions. 14 | typedef signed char sint8; 15 | typedef unsigned char uint8; 16 | typedef signed short sint16; 17 | typedef unsigned short uint16; 18 | typedef signed long sint32; 19 | typedef unsigned long uint32; 20 | typedef signed long long sint64; 21 | typedef unsigned long long uint64; 22 | 23 | // #define NULL ((void*) 0x00000000) 24 | 25 | #endif // INCLUDES_TYPES_H_ 26 | -------------------------------------------------------------------------------- /Source/Includes/Graphics/Desktop.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Desktop Widget. 3 | * Copyright 2025 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef INCLUDES_GRAPHICS_DESKTOP_HPP_ 13 | #define INCLUDES_GRAPHICS_DESKTOP_HPP_ 14 | 15 | /** 16 | * The base widget in the GUI heirachy. Responsible for mouse pointer. 17 | */ 18 | class Desktop : public Widget { 19 | public: 20 | Desktop(Canvas* canvas); 21 | 22 | void Redraw(); 23 | void HandleUIEvent(GuiEvent* eventData); 24 | 25 | using Widget::SetPosition; 26 | 27 | private: 28 | ScreenFont* font; 29 | const char* text; 30 | uint16 mouseX; 31 | uint16 mouseY; 32 | void DrawMouse(); 33 | }; 34 | 35 | #endif // INCLUDES_GRAPHICS_DESKTOP_HPP_ 36 | -------------------------------------------------------------------------------- /Source/Includes/amethyst.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Build-time kernel configuration. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include "Types.h" 8 | #include 9 | 10 | 11 | #ifndef INCLUDES_AMETHYST_H_ 12 | #define INCLUDES_AMETHYST_H_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | 19 | #define VERSION_MAJOR 2025 20 | #define VERSION_MINOR 2 21 | #define VERSION_PATCH 4 22 | 23 | /** 24 | * The device to use in early-boot debug logging. Options are 'serial_writeChar' and 'vgaConsole_putChar'. 25 | */ 26 | #define DEBUG_DEVICE serial_writeChar 27 | 28 | /** 29 | * Debug logging level. Any log level with a higher number than this won't be processed. 0 (critical) is highest, 4 (debug) is lowest. 30 | */ 31 | #define DEBUG_LEVEL LOGLEVEL_DEBUG 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | 37 | #endif // INCLUDES_AMETHYST_H_ 38 | -------------------------------------------------------------------------------- /Source/Includes/shell.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Shell. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | 9 | #ifndef INCLUDES_SHELL_HPP_ 10 | #define INCLUDES_SHELL_HPP_ 11 | 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | class ShellCommand { 20 | public: 21 | char* command; 22 | uint32 (* callback)(StandardIO*); 23 | }; 24 | 25 | class Shell { 26 | public: 27 | explicit Shell(StandardIO* stdio); 28 | 29 | /// @brief Main shell loop. 30 | void Main(); 31 | 32 | void RegisterCommand(const char* commandString, uint32 (* callback)(StandardIO*)); 33 | private: 34 | void ProcessLine(); 35 | StandardIO* stdio; 36 | LinkedList* commands; 37 | }; 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif // INCLUDES_SHELL_HPP_ 44 | -------------------------------------------------------------------------------- /Source/Includes/Drivers/qemuVga.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Driver for QEMU Standard Display Adapter. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | * 6 | * This driver is also used by Bochs (where it originated). 7 | * 8 | * 2D Capabilities: 9 | * [X] 2D Framebuffer 10 | * [-] Double Buffering 11 | * [ ] Hardware Sprites (Cursor) 12 | * [ ] Hardware Copy and Fill (Blitting) 13 | */ 14 | 15 | #include 16 | 17 | #ifndef INCLUDES_QEMUVGA_H_ 18 | #define INCLUDES_QEMUVGA_H_ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | deviceTree_Entry* qemuVga_initialise(pciBus_Entry* pciDetails); 29 | 30 | Canvas* qemuVga_getCanvas(); 31 | 32 | void qemuVga_setMode(uint16 width, uint16 height); 33 | 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif // INCLUDES_QEMUVGA_H_ 40 | -------------------------------------------------------------------------------- /Source/arch/x86_32/paging.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | #ifndef INCLUDES_PAGING_H_ 5 | #define INCLUDES_PAGING_H_ 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef struct { 12 | uint8 p : 1; 13 | uint8 rw : 1; 14 | uint8 user_or_supervisor : 1; 15 | uint8 writeThrough : 1; 16 | uint8 cacheDisable : 1; 17 | uint8 access : 1; 18 | uint8 accounting1 : 1; 19 | uint8 pageSize : 1; 20 | uint8 avail : 4; 21 | uint32 address : 20; 22 | } __attribute__((packed)) pageDirectoryEntry; 23 | 24 | typedef struct { 25 | uint8 p : 1; 26 | uint8 rw : 1; 27 | uint8 user_or_supervisor : 1; 28 | uint8 writeThrough : 1; 29 | uint8 cacheDisable : 1; 30 | uint8 access : 1; 31 | uint8 dirty : 1; 32 | uint8 pageAttributeTable : 1; 33 | uint8 global : 1; 34 | uint8 avail : 3; 35 | uint32 address : 20; 36 | } __attribute__((packed)) pageTableEntry; 37 | 38 | typedef pageDirectoryEntry* PageDirectory; 39 | 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | 45 | #endif // INCLUDES_PAGING_H_ 46 | -------------------------------------------------------------------------------- /Source/Includes/Graphics/TextConsole.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /*** 6 | * Implements a scrolling ANSI compliant terminal within a framebuffer canvas. 7 | */ 8 | class TextConsole : public Widget { 9 | public: 10 | TextConsole(Canvas* canvas, ScreenFont* font, uint32 x, uint32 y, uint16 rows, uint16 columns); 11 | void HandleUIEvent(GuiEvent* eventData); 12 | 13 | void PutChar(char c); 14 | 15 | void Redraw(); 16 | 17 | void Scroll(); 18 | 19 | private: 20 | void HandleControlSequenceIntroducer(char c); 21 | void HandleSelectGraphicsRendition(); 22 | void DecodeSGR(uint32 parameter) ; 23 | char** characterBuffer; 24 | uint32** backColourBuffer; 25 | uint32** foreColourBuffer; 26 | uint16 rows; 27 | uint16 columns; 28 | uint16 currentRow; 29 | uint16 currentColumn; 30 | ScreenFont* font; 31 | Canvas* canvas; 32 | uint32 colour; 33 | uint32 backcolour; 34 | uint32 x; 35 | uint32 y; 36 | char* csiParameters; 37 | bool isInCSI; 38 | }; 39 | -------------------------------------------------------------------------------- /Source/Includes/Graphics/canvas.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Graphical drawing canvas. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_GRAPHICS_CANVAS_H_ 8 | #define INCLUDES_GRAPHICS_CANVAS_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | typedef struct { 17 | void* framebuffer; 18 | uint32 width; 19 | uint32 height; 20 | // TODO: if we include bytes per line in here, we can make sub-canvas that can be given to widget children. 21 | } Canvas; 22 | 23 | void canvas_putPixel(Canvas* canvas, uint32 x, uint32 y, uint32 colour); 24 | void canvas_drawRect(Canvas* canvas, uint16 x, uint16 y, uint16 w, uint16 h, uint32 colour); 25 | 26 | /** 27 | * Copies width*height pixels from startx,starty to endx,endy. 28 | */ 29 | // void canvas_copy(Canvas* canvas, uint32 startx, uint32 starty, uint32 endx, uint32 endy, uint32 width, uint32 height); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif // INCLUDES_GRAPHICS_CANVAS_H_ 36 | -------------------------------------------------------------------------------- /Resources/Linker-Script.ld: -------------------------------------------------------------------------------- 1 | ENTRY (_start) 2 | 3 | SECTIONS 4 | { 5 | . = 0x00100000; 6 | /* The kernel will live at 3GB + 1MB in the virtual address space, */ 7 | /* which will be mapped to 1MB in the physical address space. */ 8 | /* Note that we page-align the sections. */ 9 | 10 | _kernel_start = .; 11 | .multiboot.data : { 12 | *(.multiboot.data) 13 | } 14 | 15 | .multiboot.text : { 16 | *(.multiboot.text) 17 | } 18 | 19 | . += 0xC0000000; 20 | /* Add a symbol that indicates the start address of the kernel. */ 21 | .text ALIGN (4K) : AT (ADDR (.text) - 0xC0000000) 22 | { 23 | *(.text) 24 | } 25 | .rodata ALIGN (4K) : AT (ADDR (.rodata) - 0xC0000000) 26 | { 27 | *(.rodata) 28 | } 29 | .data ALIGN (4K) : AT (ADDR (.data) - 0xC0000000) 30 | { 31 | *(.data) 32 | } 33 | .bss ALIGN (4K) : AT (ADDR (.bss) - 0xC0000000) 34 | { 35 | *(COMMON) 36 | *(.bss) 37 | *(.bootstrap_stack) 38 | } 39 | /* Add a symbol that indicates the end address of the kernel. */ 40 | _kernel_end = .; 41 | } 42 | -------------------------------------------------------------------------------- /Source/Includes/Structures/fifobuffer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Generic FIFO Buffer. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_STRUCTURES_FIFOBUFFER_H_ 8 | #define INCLUDES_STRUCTURES_FIFOBUFFER_H_ 9 | 10 | #include 11 | 12 | typedef struct { 13 | uint8* buffer; 14 | uint32 size; 15 | uint32 readIndex; 16 | uint32 writeIndex; 17 | } FIFOBuffer; 18 | 19 | /** 20 | * Creates a new FIFO buffer of the requested size. 21 | */ 22 | FIFOBuffer* FIFOBuffer_new(uint32 size); 23 | 24 | /** 25 | * Writes the specified number of bytes from data into the buffer. Returns the number of bytes written. 26 | */ 27 | uint32 FIFOBuffer_WriteBytes(FIFOBuffer* buffer, uint8* data, uint32 size); 28 | 29 | /** 30 | * Reads the specified number of bytes into data from the buffer. Returns the number of bytes read. 31 | */ 32 | uint32 FIFOBuffer_ReadBytes(FIFOBuffer* buffer, uint8* data, uint32 size); 33 | 34 | uint32 FIFOBuffer_ContentsSize(FIFOBuffer* buffer); 35 | 36 | #endif // INCLUDES_STRUCTURES_FIFOBUFFER_H_ 37 | -------------------------------------------------------------------------------- /Source/Includes/string.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - String functions. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_STRING_H_ 8 | #define INCLUDES_STRING_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | void string_copy(char *dest, const char* src); 17 | int string_compare(const char* a, const char* b); 18 | uint32 string_parseInt(const char* stringToParse); 19 | 20 | void string_toLower(char* string); 21 | void string_toUpper(char* string); 22 | 23 | uint32 string_length(const char* string); 24 | 25 | char** string_split(char* string, char splitter); 26 | 27 | /** 28 | * Formats a string. 29 | * @param destination Destination for the string output. Must have sufficient size. Memory allocation is the responsibility of the caller. 30 | * @param formatString TODO. 31 | */ 32 | // void string_format(char* destination, const char* formatString, ...); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | 38 | #endif // INCLUDES_STRING_H_ 39 | -------------------------------------------------------------------------------- /Source/Includes/cppsupport.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - C++ Support 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | * 6 | * This file is automatically included in compilation for all C++ files by the Makefile/GCC. 7 | */ 8 | 9 | #ifndef INCLUDES_CPPSUPPORT_HPP_ 10 | #define INCLUDES_CPPSUPPORT_HPP_ 11 | 12 | #include 13 | #include 14 | 15 | inline void *operator new(size_t size) { 16 | return memoryManager_allocate(size); 17 | } 18 | 19 | inline void *operator new[](size_t size) { 20 | return memoryManager_allocate(size); 21 | } 22 | 23 | inline void operator delete(void *p) { 24 | memoryManager_free(p); 25 | } 26 | 27 | inline void operator delete[](void *p) { 28 | memoryManager_free(p); 29 | } 30 | 31 | inline void *operator new(size_t, void *p) throw() { return p; } 32 | inline void *operator new[](size_t, void *p) throw() { return p; } 33 | inline void operator delete (void *, uint32) throw() { } 34 | inline void operator delete[](void *, uint32) throw() { } 35 | 36 | #endif // INCLUDES_CPPSUPPORT_HPP_ 37 | -------------------------------------------------------------------------------- /Source/GUI/TextLabel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | TextLabel::TextLabel(ScreenFont* font, uint32 locx, uint32 locy, Canvas* canvas, char* text, uint32 colour) 6 | { 7 | this->children = new LinkedList(); 8 | this->canvas = canvas; 9 | this->x = locx; 10 | this->y = locy; 11 | this->text = text; 12 | this->font = font; 13 | this->colour = colour; 14 | 15 | this->h = font->header->height; 16 | this->w = font->header->width * string_length(text); 17 | 18 | Redraw(); 19 | } 20 | 21 | void TextLabel::Redraw() { 22 | // TODO 23 | screenfont_drawWord(canvas, font, x, y, colour, text); 24 | } 25 | 26 | void TextLabel::HandleUIEvent(GuiEvent* eventData) { 27 | 28 | } 29 | 30 | void TextLabel::SetFont(ScreenFont* font) { 31 | this->font = font; 32 | this->h = font->header->height; 33 | this->w = font->header->width * string_length(text); 34 | 35 | Redraw(); 36 | } 37 | 38 | void TextLabel::SetText(char* text) { 39 | this->text = text; 40 | this->w = font->header->width * string_length(text); 41 | 42 | Redraw(); 43 | } 44 | -------------------------------------------------------------------------------- /Source/Includes/debug.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Kernel debugging functionality. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include "Types.h" 8 | 9 | 10 | 11 | #ifndef INCLUDES_DEBUG_H_ 12 | #define INCLUDES_DEBUG_H_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /** 19 | * Writes kernel debugging information to the kernel's debug log (serial port in early boot, log files once booted). 20 | * @param logLevel The level of the log message. 21 | * @param formatString printf()-formatted message. 22 | */ 23 | void debug(uint8 logLevel, const char* formatString, ...); 24 | 25 | #define LOGLEVEL_CRITICAL 0 26 | #define LOGLEVEL_ERROR 1 27 | #define LOGLEVEL_WARNING 2 28 | #define LOGLEVEL_INFO 3 29 | #define LOGLEVEL_DEBUG 4 30 | 31 | /** 32 | * Recommended level for high-volume messages such as internal memory manager and scheduler decisions. 33 | */ 34 | #define LOGLEVEL_TRACE 5 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif // INCLUDES_DEBUG_H_ 41 | -------------------------------------------------------------------------------- /Source/Includes/Drivers/serial.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Driver for ISA 16550 UART. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_SERIAL_H_ 8 | #define INCLUDES_SERIAL_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #define SERIAL_COM1 0x03F8 17 | #define SERIAL_COM2 0x02F8 18 | 19 | #define SERIAL_BAUD_115200 0x01 20 | #define SERIAL_BAUD_57600 0x02 21 | #define SERIAL_BAUD_38400 0x03 22 | #define SERIAL_BAUD_28800 0x04 23 | #define SERIAL_BAUD_19200 0x06 24 | #define SERIAL_BAUD_14400 0x08 25 | #define SERIAL_BAUD_9600 0x0C 26 | 27 | int serial_detect(uint16 baseAddress); 28 | int serial_init(uint16 baseAddress, uint8 divisor); 29 | char serial_readByte(void); 30 | 31 | int serial_canRead(void); 32 | char serial_readChar(void); 33 | char* serial_readString(void); 34 | 35 | void serial_writeChar(char a); 36 | 37 | void serial_writeString(const char* stringToOutput); 38 | void serial_writeLine(const char* stringToOutput); 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif // INCLUDES_SERIAL_H_ 45 | -------------------------------------------------------------------------------- /Source/Includes/StandardIO.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Stream I/O definition. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_STANDARDIO_HPP_ 8 | #define INCLUDES_STANDARDIO_HPP_ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /// @brief Represents the standard I/O streams and provides access to them. 18 | class StandardIO { 19 | public: 20 | /// @brief Creates a new StandardIO instance using the specified stdin and stdout character devices. 21 | /// @param stdout A function that takes a single character. 22 | /// @param stdin A function that returns a single character. 23 | StandardIO(void (*stdout)(char), char (*stdin)(void)); 24 | 25 | /// @brief Formatted printing. 26 | /// @param formatString 27 | /// @param 28 | void Print(const char* formatString, ...); 29 | 30 | char* ReadLine(bool echo); 31 | 32 | void (*stdout)(char); 33 | 34 | private: 35 | char (*stdin)(void); 36 | }; 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif // INCLUDES_STANDARDIO_HPP_ 43 | -------------------------------------------------------------------------------- /Source/Includes/physicalMemory.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Physical Memory Management. 3 | * Copyright 2025 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_PHYSICALMEMORY_H_ 8 | #define INCLUDES_PHYSICALMEMORY_H_ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | extern uint32 _kernel_end; 18 | uint32 physicalMemory_findEndOfReservedMemory(struct multiboot_moduleNode* module, uint32 count); 19 | 20 | /** 21 | * Initialises the physical memory manager. 22 | */ 23 | void physicalMemory_initialise(struct multiboot_info* multibootData); 24 | 25 | /** 26 | * Allocates a 4K block of physical RAM and returns it's physical address. 27 | */ 28 | void* physicalMemory_allocate(); 29 | 30 | /** 31 | * Marks a specific 4K block of physical memory as allocated. DO WE NEED THIS?! 32 | */ 33 | void physicalMemory_markAllocated(void* address); 34 | 35 | /** 36 | * Marks a specific 4K block of physical memory as allocated. 37 | */ 38 | void physicalMemory_free(void* address); 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif // INCLUDES_PHYSICALMEMORY_H_ 45 | 46 | -------------------------------------------------------------------------------- /Source/Includes/Graphics/screenfont.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - PC Screen Font. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_GRAPHICS_SCREENFONT_H_ 8 | #define INCLUDES_GRAPHICS_SCREENFONT_H_ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct { 18 | uint32 magicBytes; 19 | uint32 version; 20 | uint32 headerSize; 21 | uint32 flags; 22 | uint32 length; // number of glyphs 23 | uint32 glyphSize; // bytes per glyph 24 | uint32 height; // pixels 25 | uint32 width; // pixels 26 | } ScreenFontHeader; 27 | 28 | typedef struct { 29 | ScreenFontHeader* header; 30 | uint8* characterData; 31 | } ScreenFont; 32 | 33 | 34 | void screenfont_drawChar(Canvas* canvas, ScreenFont* font, uint16 x, uint16 y, uint32 colour, char a); 35 | void screenfont_drawWord(Canvas* canvas, ScreenFont* font, uint16 x, uint16 y, uint32 colour, const char* a); 36 | 37 | #define SCREENFONT_MAGIC 0x72B54A86 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif // INCLUDES_GRAPHICS_SCREENFONT_H_ 44 | -------------------------------------------------------------------------------- /Source/Includes/Graphics/Widget.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Base UI Element. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_GRAPHICS_WIDGET_HPP_ 8 | #define INCLUDES_GRAPHICS_WIDGET_HPP_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /** 16 | * Abstract class for a graphical widget. Has an X and Y, width and height, knows how to draw itself and accept UI events, and has children it can draw and pass events to. 17 | */ 18 | class Widget { 19 | public: 20 | virtual void Redraw() = 0; 21 | virtual void HandleUIEvent(GuiEvent* eventData) = 0; 22 | 23 | void SetPosition(sint32 x, sint32 y); 24 | 25 | // Children functions. 26 | void AddChild(Widget* widget); 27 | void RemoveChild(Widget* widget); 28 | 29 | protected: 30 | Canvas* canvas; 31 | Widget* parent; 32 | LinkedList* children; 33 | sint32 x; 34 | sint32 y; 35 | uint32 h; 36 | uint32 w; 37 | 38 | void RedrawChildren(); 39 | void SendGuiEventToChildren(GuiEvent* eventData, uint32 x, uint32 y); 40 | }; 41 | 42 | #endif // INCLUDES_GRAPHICS_WIDGET_HPP_ 43 | -------------------------------------------------------------------------------- /Source/Includes/stream.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Stream definition. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_STREAM_H_ 8 | #define INCLUDES_STREAM_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /** 19 | * Prints a formatted string to the specified putChar function (the putChar should take a single argument which is a char). 20 | * %d - decimal 21 | * %h - hex, no padding 22 | * %H - hex, padding 23 | * %s - string 24 | */ 25 | void stream_printf(void (*putChar)(char), const char* formatString, ...); 26 | 27 | // Internal version of the above, mostly for use with debug etc. 28 | void stream_vprintf(void (*putChar)(char), const char* formatString, va_list args); 29 | 30 | char* stream_readLine(char (*getChar)(void), void (*putChar)(char), bool echoMode); 31 | 32 | /** 33 | * An input/output stream. If buffer is null, all writes are immediate. 34 | */ 35 | typedef struct { 36 | FIFOBuffer* buffer; 37 | void (*flush)(char); 38 | } Stream; 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif // INCLUDES_STREAM_H_ 45 | -------------------------------------------------------------------------------- /Source/arch/x86_32/gdt.S: -------------------------------------------------------------------------------- 1 | #This is the code to load my manually built GDT table into the memory. 2 | 3 | .global gdt_install 4 | .global gdt_table 5 | .global gdt_pointer 6 | 7 | gdt_install: #Installs a new GDT and reloads segment registers. 8 | lgdt gdt_pointer 9 | movw $0x10,%ax 10 | movw %ax,%ds 11 | movw %ax,%es 12 | movw %ax,%fs 13 | movw %ax,%gs 14 | movw %ax,%ss 15 | ljmp $0x08, $gdtFlush 16 | gdtFlush: 17 | ret 18 | 19 | .align 32 20 | gdt_table: #The actual GDT table. 21 | .long 0x00000000 #Empty descriptor. 22 | .long 0x00000000 23 | 24 | .long 0x0000FFFF #Code descriptor (ring 0). 0GB->4GB. 25 | .long 0x00CF9A00 26 | 27 | .long 0x0000FFFF #Data descriptor (ring 0). 0GB->4GB. 28 | .long 0x00CF9200 29 | 30 | .long 0x0000FFFF #Code descriptor (ring 3). 0GB->4GB. 31 | .long 0x00CFFA00 32 | 33 | .long 0x0000FFFF #Data descriptor (ring 3). 0GB->4GB. 34 | .long 0x00CFF200 35 | 36 | .long 0x20000072 #Task State Segment - TSS structure located at 0x00002000 physical. 37 | .long 0x0040E900 38 | 39 | # Pointer we give the CPU to tell it where the GDT table is and how big it is. 40 | .align 32 41 | gdt_pointer: 42 | .word 47 # Size of GDT table (in bytes), minus 1. 43 | .long gdt_table # The location of the GDT table. 44 | -------------------------------------------------------------------------------- /Source/GUI/Window.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | Window::Window(ScreenFont* font, uint32 locx, uint32 locy, uint32 w, uint32 h, Canvas* canvas, const char* text) 8 | { 9 | this->children = new LinkedList(); 10 | this->canvas = canvas; 11 | this->x = locx; 12 | this->y = locy; 13 | this->text = text; 14 | this->font = font; 15 | 16 | this->h = h; 17 | this->w = w; 18 | 19 | // TextLabel* label = new TextLabel(font, 8, 8, canvas, text, 0x00FFFFFF); 20 | 21 | // children->Add(label); 22 | 23 | Redraw(); 24 | } 25 | 26 | void Window::Redraw() { 27 | // Window Decoration 28 | canvas_drawRect(canvas, x, y, w, 32, 0x008000C0); 29 | 30 | canvas_drawRect(canvas, x + w - 24, y + 8, 16, 16, 0x00FF0000); 31 | canvas_drawRect(canvas, x + w - 32- 24, y + 8, 16, 16, 0x00FF00FF); 32 | canvas_drawRect(canvas, x + w - 32- 32-24, y + 8, 16, 16, 0x00FFFF00); 33 | 34 | uint32 textx = (w / 2) - (8 * string_length(text) / 2); 35 | 36 | screenfont_drawWord(canvas, font, x + textx, y + 8, 0xFFFFFFFF, text); 37 | 38 | RedrawChildren(); 39 | } 40 | 41 | void Window::HandleUIEvent(GuiEvent* eventData) { 42 | debug(LOGLEVEL_ERROR, "WINDOW HANDLING EVENT?!"); 43 | } 44 | 45 | void Window::SetName(char* text) { 46 | this->text = text; 47 | //this->w = font->header->width * string_length(text); 48 | 49 | Redraw(); 50 | } 51 | -------------------------------------------------------------------------------- /Source/arch/x86_32/portIO.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Port I/O. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | uint8 portIO_read8(uint16 portNumber) { 11 | uint8 value; 12 | 13 | __asm__ __volatile__( 14 | "inb %w1,%b0" 15 | :"=a"(value) 16 | :"d"(portNumber) 17 | ); 18 | 19 | return value; 20 | } 21 | 22 | void portIO_write8(uint16 portNumber, uint8 value) { 23 | __asm__ __volatile__( 24 | "outb %b0,%w1" 25 | : 26 | :"a"(value), "d"(portNumber) 27 | ); 28 | } 29 | 30 | uint16 portIO_read16(uint16 portNumber) { 31 | uint16 value; 32 | 33 | __asm__ __volatile__( 34 | "inw %w1,%w0" 35 | :"=a"(value) 36 | : "d"(portNumber) 37 | ); 38 | 39 | return value; 40 | } 41 | 42 | void portIO_write16(uint16 portNumber, uint16 value) { 43 | __asm__ __volatile__( 44 | "outw %w0,%w1" 45 | : 46 | : "a"(value), "d"(portNumber) 47 | ); 48 | } 49 | 50 | uint32 portIO_read32(uint16 portNumber) { 51 | uint32 value; 52 | 53 | __asm__ __volatile__( 54 | "inl %w1,%0" 55 | :"=a"(value) 56 | :"d"(portNumber) 57 | ); 58 | 59 | return value; 60 | } 61 | 62 | void portIO_write32(uint16 portNumber, uint32 value) { 63 | __asm__ __volatile__( 64 | "outl %0,%w1" 65 | : 66 | :"a"(value), "d"(portNumber) 67 | ); 68 | } 69 | 70 | void haltCPU(void) { 71 | __asm__ __volatile__("hlt"); 72 | } 73 | -------------------------------------------------------------------------------- /Docs/Streams.md: -------------------------------------------------------------------------------- 1 | # Streams 2 | 3 | One of the fundamental constructs within the operating system will be streams (and one we'll need quite early on for text input and output to the user). A stream is an object that can either be a source of byte data, or a destination for byte data. Bytes are written one at a time or read one at a time. 4 | 5 | Streams cannot be seeked (otherwise they're just files). 6 | 7 | Streams can be read (keyboard) or write (vga), or both (serial). 8 | 9 | Streams are ANSI terminal escape compliant (so keyboard sequences can be encoded for passing up to applications). 10 | 11 | stream_getMode(); // stream can be read from, written to, or both. 12 | 13 | stream_canRead(); // buffer has data. 14 | stream_readChar(); 15 | stream_readString(); // reads until buffer is empty. 16 | stream_readLine(); // reads until an enter is hit. 17 | 18 | stream_canWrite(); // buffer not full. 19 | stream_writeChar(); 20 | stream_writeString(); // writes until a /0. 21 | stream_writeLine(); // writeString but with a /n at the end. 22 | 23 | streams would be easier with C++ classes? 24 | 25 | STREAM_CANREAD = 0 26 | STREAM_CANWRITE =1 27 | 28 | ANSI streams: carriage return is idempotent, new line/line feed is not. 29 | 30 | StreamReader has support functions more than ReadByte()/ReadBytes() 31 | StreamWriter ditto 32 | 33 | 34 | Classes: 35 | Stream 36 | StreamReader 37 | StreamWriter 38 | 39 | MemoryStream (FIFO Buffer) 40 | SerialStream 41 | new SerialStream(serialDevice); 42 | ConsoleStream 43 | new ConsoleStream(vgaDevice, keyboardDevice); -------------------------------------------------------------------------------- /Source/graphics.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Random graphics functions. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | void canvas_putPixel(Canvas* canvas, uint32 x, uint32 y, uint32 colour) { 11 | uint32* screen = canvas->framebuffer; 12 | screen[(y * canvas->width) + x] = colour; 13 | } 14 | 15 | void canvas_drawRect(Canvas* canvas, uint16 x, uint16 y, uint16 w, uint16 h, uint32 colour) { 16 | for (uint16 i = 0; i < h; i++) { 17 | for (uint16 j = 0; j < w; j++) { 18 | canvas_putPixel(canvas, x + j, y + i, colour); 19 | } 20 | } 21 | } 22 | 23 | void screenfont_drawChar(Canvas* canvas, ScreenFont* font, uint16 x, uint16 y, uint32 colour, char a) { 24 | // go to top level. 25 | uint8* charData = font->characterData; 26 | charData += a * font->header->glyphSize; 27 | 28 | for (uint32 r = 0; r < font->header->height; r++) { 29 | // draw each row. 30 | 31 | for (uint32 c = 0; c < font->header->width; c++) { 32 | // draw each column/pixel. 33 | char yes = (charData[r] << c) & 0x80; 34 | 35 | if (yes) { 36 | canvas_putPixel(canvas, x + c, y + r, colour); 37 | } 38 | } 39 | } 40 | } 41 | 42 | void screenfont_drawWord(Canvas* canvas, ScreenFont* font, uint16 x, uint16 y, uint32 colour, const char* a) { 43 | int i = 0; 44 | 45 | while (a[i]) { 46 | screenfont_drawChar(canvas, font, x + (i*font->header->width), y, colour, a[i]); 47 | i++; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/debug.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Debug functions. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void egaPutChar(char out); 15 | 16 | void debug(uint8 logLevel, const char* formatString, ...) { 17 | va_list args; 18 | va_start(args, formatString); 19 | 20 | if (logLevel > DEBUG_LEVEL) { 21 | return; 22 | } 23 | 24 | switch (logLevel) { 25 | case 0: 26 | stream_printf(DEBUG_DEVICE, "CRIT: "); 27 | break; 28 | 29 | case 1: 30 | stream_printf(DEBUG_DEVICE, "ERROR: "); 31 | break; 32 | 33 | case 2: 34 | stream_printf(DEBUG_DEVICE, "WARN: "); 35 | break; 36 | 37 | case 3: 38 | stream_printf(DEBUG_DEVICE, "INFO: "); 39 | break; 40 | 41 | case 4: 42 | stream_printf(DEBUG_DEVICE, "DEBUG: "); 43 | break; 44 | } 45 | 46 | stream_vprintf(DEBUG_DEVICE, formatString, args); 47 | stream_vprintf(egaPutChar, formatString, args); 48 | stream_printf(DEBUG_DEVICE, "\n"); 49 | stream_printf(egaPutChar, "\n"); 50 | 51 | va_end(args); 52 | } 53 | 54 | char* egaFB = (char*) 0xC00B8000; 55 | 56 | uint32 egaPos = 0; 57 | 58 | void egaPutChar(char out) { 59 | if (out == '\n') { 60 | egaPos = 0; 61 | } else { 62 | egaFB[egaPos] = out; 63 | egaPos++; 64 | egaFB[egaPos] = 0x0F; 65 | egaPos++; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Source/Includes/portIO.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Basic I/O port handling functions. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_PORTIO_H_ 8 | #define INCLUDES_PORTIO_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** 17 | * Reads an 8-bit value from the specified port. 18 | * @param portNumber Port to read from. 19 | * @return Byte that was read. 20 | */ 21 | uint8 portIO_read8(uint16 portNumber); 22 | 23 | /** 24 | * Writes an 8-bit value to the specified port. 25 | * @param portNumber Port to write to. 26 | * @param value The value to write. 27 | */ 28 | void portIO_write8(uint16 portNumber, uint8 Value); 29 | 30 | /** 31 | * Reads a 16-bit value from the specified port. 32 | * @param portNumber Port to read from. 33 | * @return Value that was read. 34 | */ 35 | uint16 portIO_read16(uint16 portNumber); 36 | 37 | /** 38 | * Writes a 16-bit value to the specified port. 39 | * @param portNumber Port to write to. 40 | * @param value The value to write. 41 | */ 42 | void portIO_write16(uint16 portNumber, uint16 value); 43 | 44 | /** 45 | * Reads a 32-bit value from the specified port. 46 | * @param portNumber Port to read from. 47 | * @return Value that was read. 48 | */ 49 | uint32 portIO_read32(uint16 portNumber); 50 | 51 | /** 52 | * Writes a 32-bit value to the specified port. 53 | * @param portNumber Port to write to. 54 | * @param value The value to write. 55 | */ 56 | void portIO_write32(uint16 portNumber, uint32 value); 57 | 58 | /** 59 | * Halts the CPU. 60 | */ 61 | void haltCPU(void); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | #endif // INCLUDES_PORTIO_H_ 68 | -------------------------------------------------------------------------------- /Source/GUI/Widget.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void Widget::SetPosition(sint32 x, sint32 y) { 8 | this->x = x; 9 | this->y = y; 10 | } 11 | 12 | void Widget::AddChild(Widget* widget) { 13 | children->Add(widget); 14 | } 15 | 16 | void Widget::RemoveChild(Widget* widget) { 17 | children->Remove(widget); 18 | } 19 | 20 | void Widget::RedrawChildren() 21 | { 22 | debug(LOGLEVEL_TRACE, "Performing Widget Redraw"); 23 | children->Reset(); 24 | debug(LOGLEVEL_TRACE, "after reset"); 25 | 26 | do { 27 | Widget* child = children->Current(); 28 | 29 | debug(LOGLEVEL_TRACE, "Child?"); 30 | 31 | if(child != nullptr) { 32 | child->Redraw(); 33 | } 34 | 35 | } while(children->Next()); 36 | } 37 | 38 | void Widget::SendGuiEventToChildren(GuiEvent* eventData, uint32 x, uint32 y) { 39 | debug(LOGLEVEL_TRACE, "Performing Event Pushdown"); 40 | children->Reset(); 41 | 42 | do { 43 | Widget* child = children->Current(); 44 | 45 | debug(LOGLEVEL_TRACE, "Child?"); 46 | 47 | if(child != nullptr) { 48 | debug(LOGLEVEL_TRACE, "Event X: %h, Child X: %h, Child W: %h", x, child->x, child->w); 49 | debug(LOGLEVEL_TRACE, "Event Y: %h, Child Y: %h, Child H: %h", y, child->y, child->h); 50 | 51 | 52 | if(x >= child->x && x <= child->x + child->w) { 53 | debug(LOGLEVEL_TRACE, "X"); 54 | if(y >= child->y && y <= child->y + child->h) { 55 | debug(LOGLEVEL_TRACE, "Y"); 56 | child->HandleUIEvent(eventData); 57 | } 58 | } 59 | } 60 | 61 | } while(children->Next()); 62 | } -------------------------------------------------------------------------------- /Source/Includes/memoryManager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Memory management stuff (so far). 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_MEMORYMANAGER_H_ 8 | #define INCLUDES_MEMORYMANAGER_H_ 9 | 10 | #include 11 | #include "../arch/x86_32/paging.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | // We can't use zero as an invalid pointer location in the memory manager (because it's valid). 18 | // Use the end of memory instead. 19 | // Client code can assume zero is a bad pointer, but we can't. 20 | #define END_OF_MEMORY_LIST 0xFFFFFFFF 21 | 22 | typedef struct memoryManager_freeMemoryNode_s { 23 | uint64 address; 24 | uint64 length; 25 | struct memoryManager_freeMemoryNode_s* next; 26 | } memoryManager_freeMemoryNode; // Kept in order of size, small to large. 27 | 28 | typedef struct { 29 | uint32 size; 30 | } memoryManager_allocatedHeader; 31 | 32 | void memoryManager_init(struct multiboot_memoryMapNode* startAddress, uint32 length, uint32 endOfReservedMemory); 33 | 34 | /** 35 | * Returns the address of the current page directory. 36 | */ 37 | PageDirectory* memoryManager_getCurrentPageDirectory(); 38 | 39 | void memoryManager_printMemoryMap(PageDirectory* directory); 40 | 41 | /** 42 | * Maps count number of 4KiB regions of physical memory into logical memory. 43 | */ 44 | void memoryManager_mapPhysicalMemoryPage(PageDirectory* directory, void* startLogicalAddress, void* physicalMemory, uint32 count); 45 | 46 | void* memoryManager_allocate(uint32 size); 47 | void memoryManager_free(void* mem); 48 | 49 | void memoryManager_debug_printFreeMemoryList(void); 50 | 51 | 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | 56 | #endif // INCLUDES_MEMORYMANAGER_H_ 57 | 58 | -------------------------------------------------------------------------------- /Source/GUI/Desktop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | Desktop::Desktop(Canvas* canvas) 8 | { 9 | this->children = new LinkedList(); 10 | this->canvas = canvas; 11 | this->text = text; 12 | this->font = font; 13 | 14 | this->x = 0; 15 | this->y = 0; 16 | this->h = canvas->height; 17 | this->w = canvas->width; 18 | 19 | this->mouseX = canvas->width / 2; 20 | this->mouseY = canvas->height / 2; 21 | 22 | Redraw(); 23 | } 24 | 25 | void Desktop::Redraw() { 26 | RedrawChildren(); 27 | DrawMouse(); 28 | } 29 | 30 | void Desktop::HandleUIEvent(GuiEvent* eventData) { 31 | 32 | switch(eventData->GetEventType()) 33 | { 34 | case GuiEventType::MOUSE_CLICK: 35 | debug(LOGLEVEL_DEBUG, "CLICK"); 36 | // TODO: find out which child item has been clicked, and pass the click down to that child so it can process the event there. 37 | SendGuiEventToChildren(eventData, mouseX, mouseY); 38 | break; 39 | 40 | case GuiEventType::MOUSE_MOVE: { 41 | MouseMoveEvent* move = (MouseMoveEvent*) eventData; 42 | this->mouseX = this->mouseX + move->getMouseX(); 43 | this->mouseY = this->mouseY + move->getMouseY(); 44 | 45 | if(this->mouseX >= this->w) { 46 | this->mouseX = this->w - 1; 47 | } 48 | 49 | if(this->mouseY >= this->h) { 50 | this->mouseY = this->h - 1; 51 | } 52 | break;} 53 | 54 | default: 55 | debug(LOGLEVEL_ERROR, "UNKNOWN GUI EVENT!?"); 56 | break; 57 | } 58 | 59 | Redraw(); 60 | } 61 | 62 | void Desktop::DrawMouse() { 63 | canvas_drawRect(canvas, mouseX, mouseY, 1, 8, 0xFFFFFFFF); 64 | canvas_drawRect(canvas, mouseX, mouseY, 8, 1, 0xFFFFFFFF); 65 | } 66 | -------------------------------------------------------------------------------- /Source/Includes/GDT.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Functions interacting with the system GDT table. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_GDT_H_ 8 | #define INCLUDES_GDT_H_ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /** 15 | * Installs a basic GDT table. 16 | */ 17 | 18 | /// @brief Installs the basic GDT. 19 | void gdt_install(); 20 | 21 | typedef struct { 22 | unsigned int limit_low : 16; 23 | unsigned int base_low : 24; 24 | 25 | /// @brief For system segments, the type of segment. For code/data segments, holds access bit, read/write bit, conforming bit, and executable bit. 26 | unsigned int access : 4; 27 | 28 | unsigned int code_data_segment : 1; // should be 1 for everything but TSS and LDT 29 | unsigned int DPL : 2; // privilege level 30 | unsigned int present : 1; 31 | unsigned int limit_high : 4; 32 | unsigned int available : 1; // only used in software; has no effect on hardware 33 | unsigned int long_mode : 1; 34 | unsigned int big : 1; // 32-bit opcodes for code, uint32_t stack for data 35 | unsigned int gran : 1; // 1 to use 4k page addressing, 0 for byte addressing 36 | unsigned int base_high : 8; 37 | } __attribute__((packed)) gdt_entry; 38 | 39 | /// @brief Information about the Global Descriptor Table, loadable by LGDT. 40 | typedef struct { 41 | uint16 size; 42 | uint32 offset; 43 | } __attribute__((packed)) gdt_descriptor; 44 | 45 | extern gdt_descriptor gdt_pointer; 46 | 47 | extern gdt_entry gdt_table[6]; 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | 53 | #endif // INCLUDES_GDT_H_ 54 | -------------------------------------------------------------------------------- /Source/arch/x86_32/thread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | extern void* boot_page_directory; 7 | 8 | // Current TCB for this CPU. 9 | thread_control_block* current_task_TCB; 10 | 11 | // TODO: make this thread-safe. 12 | bool schedulerEnabled = false; 13 | 14 | thread_control_block* new_task(void (* callback)(), thread_control_block* currentTask) { 15 | debug(LOGLEVEL_INFO, "Creating new thread..."); 16 | 17 | thread_control_block* tcb = (thread_control_block*)memoryManager_allocate(sizeof(thread_control_block)); 18 | 19 | uint32* stack = (uint32*)memoryManager_allocate(sizeof(uint32) * 1024); // 4KiB stack (one page). - later this will be an actual page allocation. 20 | 21 | debug(LOGLEVEL_DEBUG, "stack: %h", stack); 22 | 23 | // Set up the initial stack frame. 24 | stack[1023] = (uint32)callback; // EIP 25 | stack[1022] = 0; // EBX; 26 | stack[1021] = 0; // ESI; 27 | stack[1020] = 0; // EDI; 28 | stack[1019] = (uint32)(stack + 1023); // EBP 29 | 30 | tcb->kernel_stack_top = (void*)(stack + 1019); 31 | tcb->cr3 = currentTask->cr3; 32 | 33 | return tcb; 34 | } 35 | 36 | thread_control_block* initialise_multitasking() { 37 | current_task_TCB = memoryManager_allocate(sizeof(thread_control_block)); 38 | 39 | current_task_TCB->cr3 = (uint32) &boot_page_directory - 0xC0000000; 40 | current_task_TCB->process_control_block = NULL; 41 | 42 | // TODO: set up the linked lists for thread control block storage. 43 | 44 | return current_task_TCB; 45 | } 46 | 47 | void thread_startScheduler() { 48 | schedulerEnabled = true; 49 | } 50 | 51 | void thread_stopScheduler() { 52 | schedulerEnabled = false; 53 | } 54 | 55 | void scheduler() { 56 | if(schedulerEnabled) { 57 | thread_control_block* nextThread = current_task_TCB->nextThread; 58 | 59 | if(nextThread != NULL) { 60 | // debug(LOGLEVEL_DEBUG, "We would switch task here..."); 61 | switch_to_task(nextThread); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Docs/Architecture.md: -------------------------------------------------------------------------------- 1 | # System Architecture - Brief Notes for Now 2 | 3 | ## Kernel / Userland 4 | 5 | The kernel will be a microkernel. Drivers will exist in userland. 6 | 7 | The microkernel will be responsible for the following things: 8 | 9 | - Scheduling 10 | - Memory Management (Physical and Virtual) 11 | - Resource Management (Interrupts, I/O Space, etc) 12 | 13 | All other tasks (including paging virtual memory to disk) will be handled by userspace services. 14 | 15 | The kernel will present three different APIs to the userland: 16 | 17 | - Driver API 18 | - POSIX API 19 | - Amethyst Native API 20 | 21 | All three will use the same ABI (kernel will provide all calls for all processes) but statically linked library will be different. 22 | 23 | ## Process Model 24 | 25 | Use kernel threading in 1:1:N model (kernel knows about all threads, but has no idea about fibres). 26 | 27 | Session: Console, Security 28 | Process: Code / Data / Heap (Virtual Address Space - CR3), File Pointers, working directory etc 29 | Thread: Registers / Stack (Stack Top) 30 | Fibres: User-mode co-operative thread. Invisible to the kernel, but implemented in the standard library. 31 | 32 | Session is a collection of processes sharing a security context (user, etc) and user interface (GUI / terminal session / etc). 33 | Process is code/data/heap shared. 34 | Thread is instruction pointer and stack. 35 | 36 | Kernel will natively support threads. 37 | 38 | Threads have priorities, processes have priorities, sessions have priorities (driver session has highest priority, for example). 39 | 40 | Each has 16 priority levels, with 15 being the highest. Process priority is left shifted two bits, session priority is left shifted 4 bits, then added together and maxed to 255. 41 | Idle task is priority 0. 42 | 43 | ## IPC 44 | 45 | - messages 46 | - shared memory 47 | 48 | drivers are allowed to request shared memory with the HAL for use with devices. 49 | 50 | ## Memory 51 | 52 | Paging is used. 0GB-3GB is userspace. 3GB-4GB is kernelspace. The first 4MB of physical memory is always mapped to the kernel. 53 | -------------------------------------------------------------------------------- /Source/Includes/deviceTree.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Definitions for kernel device driver tree. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | 9 | #ifndef INCLUDES_DEVICETREE_H_ 10 | #define INCLUDES_DEVICETREE_H_ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | typedef struct { 17 | uint32 Type; // 0 = mem, 1 = i/o, 2, irq. 18 | uint32 Flags; // irq number for irq. memory prefetch etc for memory. and so on. 19 | uint32 StartAddress; // Used for memory and Io 20 | uint32 Length; // Used for memory and Io 21 | } DeviceResource; 22 | 23 | /** 24 | * An entry in the kernel device driver tree. Parents are responsible for detecting and initialising their children. 25 | */ 26 | typedef struct deviceTree_Entry_S { 27 | struct deviceTree_Entry_S* next; // pointer to next sibling of this device. 28 | struct deviceTree_Entry_S* child; // pointer to first child. 29 | char* name; 30 | uint32 type; // determines what sort of struct data points to. 31 | DeviceResource* Resources; // as resources are unlikely to change much once a device is initialised, we use an array rather than a linked list. 32 | uint32 ResourceCount; 33 | void* data; 34 | } deviceTree_Entry; 35 | 36 | // This method is provided by the root platform device. 37 | void deviceTree_build(void); 38 | 39 | // This method is provided by the root platform device. 40 | deviceTree_Entry* deviceTree_get(void); 41 | 42 | // Prints. Provided by deviceTree.c 43 | void deviceTree_print(void (*putChar)(char), bool detailedInfo); 44 | 45 | deviceTree_Entry* deviceTree_createDevice(char* name, uint32 type, void* data); 46 | void deviceTree_addSibling(deviceTree_Entry* attached, deviceTree_Entry* toAttach); 47 | void deviceTree_addChild(deviceTree_Entry* parent, deviceTree_Entry* toAttach); 48 | 49 | #define DEVICETREE_TYPE_UNKNOWN 0x00 50 | #define DEVICETREE_TYPE_PCI 0x01 51 | // ... 52 | #define DEVICETREE_TYPE_OTHER 0xFFFFFFFF 53 | 54 | #define DEVICE_RESOURCETYPE_MEM 0x00 55 | #define DEVICE_RESOURCETYPE_IO 0x01 56 | #define DEVICE_RESOURCETYPE_IRQ 0x02 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | 62 | #endif // INCLUDES_DEVICETREE_H_ 63 | -------------------------------------------------------------------------------- /Source/Includes/Drivers/pciBus.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - PCI Bus Driver. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | * 6 | * This driver supports detection and enumeration of the PCI bus (including PCI Express, AGP and CardBus) and detection 7 | * and initialisation of child devices (such as display adapters, network controllers, storage controllers, etc). 8 | */ 9 | 10 | #ifndef INCLUDES_DRIVERS_PCIBUS_H_ 11 | #define INCLUDES_DRIVERS_PCIBUS_H_ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | /** 21 | * Performs detection and enumeration of the PCI buses, and initialises any found child devices. 22 | */ 23 | deviceTree_Entry* pciBus_initialise(void); 24 | 25 | void pci_enumerateBuses(void); 26 | void pci_checkBus(uint8 bus); 27 | void pci_checkSlot(uint8 bus, uint8 slot); 28 | deviceTree_Entry* pci_addDevicesToTree(void); 29 | 30 | /** 31 | * Reads the PCI configuration registers for a particular PCI device. 32 | * @param bus PCI bus number. 8 bits. 33 | * @param slot Which slot (device) on that PCI bus. 5 bits. 34 | * @param function Which function of that slot/device. 3 bits. 35 | * @param registerNo Which register to read. Must be a multiple of 4. 6 bits. 36 | * @return The contents of the specified register for that bus/slot/function. 37 | */ 38 | uint32 pci_readConfigurationRegister(uint8 bus, uint8 slot, uint8 function, uint8 registerNo); 39 | 40 | void pci_writeConfigurationRegister(uint8 bus, uint8 slot, uint8 function, uint8 registerNo, uint32 data); 41 | uint32 pci_calculateRegisterAddress(uint8 bus, uint8 slot, uint8 function, uint8 registerNo); 42 | uint32 pci_getBar(uint8 bus, uint8 slot, uint8 function, uint8 bar); 43 | uint32 pci_getBarSize(uint8 bus, uint8 slot, uint8 function, uint8 bar); 44 | 45 | typedef struct pciBus_Entry_s { 46 | uint8 bus; 47 | uint8 slot; 48 | uint8 function; 49 | uint16 vendorID; 50 | uint16 deviceID; 51 | uint8 classID; 52 | uint8 subClassID; 53 | struct pciBus_Entry_s* next; 54 | } pciBus_Entry; 55 | 56 | void pciBus_printDeviceInformation(void (*putChar)(char), pciBus_Entry* device, uint32 depth); 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | 62 | #endif // INCLUDES_DRIVERS_PCIBUS_H_ 63 | -------------------------------------------------------------------------------- /Source/arch/x86_32/taskswitch.asm: -------------------------------------------------------------------------------- 1 | ;C declaration: 2 | ; void switch_to_task(thread_control_block *next_thread); 3 | ; 4 | ;WARNING: Caller is expected to disable IRQs before calling, and enable IRQs again after function returns 5 | 6 | extern current_task_TCB 7 | global switch_to_task 8 | 9 | switch_to_task: 10 | 11 | ;Save previous task's state 12 | 13 | ;Notes: 14 | ; For cdecl; EAX, ECX, and EDX are already saved by the caller and don't need to be saved again 15 | ; EIP is already saved on the stack by the caller's "CALL" instruction 16 | ; The task isn't able to change CR3 so it doesn't need to be saved 17 | ; Segment registers are constants (while running kernel code) so they don't need to be saved 18 | 19 | push ebx 20 | push esi 21 | push edi 22 | push ebp 23 | 24 | mov edi,[current_task_TCB] ;edi = address of the previous task's "thread control block" 25 | mov [edi+0],esp ;Save ESP for previous task's kernel stack in the thread's TCB 26 | 27 | ; Switch current thread control block. 28 | 29 | mov esi,[esp+(4+1)*4] ;esi = address of the next task's "thread control block" (parameter passed on stack) - 4 is what we just pushed above, 1 is the ret for this function. 30 | mov [current_task_TCB],esi ;Current task's TCB is the next task TCB 31 | 32 | ; Load next thread's state into the CPU. 33 | 34 | mov esp,[esi+0] ;Load ESP for next task's kernel stack from the thread's TCB 35 | mov eax,[esi+4] ;eax = address of page directory for next task 36 | ; mov ebx,[esi+TCB.ESP0] ;ebx = address for the top of the next task's kernel stack 37 | ; mov [TSS.ESP0],ebx ;Adjust the ESP0 field in the TSS (used by CPU for for CPL=3 -> CPL=0 privilege level changes) 38 | mov ecx,cr3 ;ecx = previous task's virtual address space 39 | 40 | cmp eax,ecx ;Does the virtual address space need to being changed? 41 | je .doneVAS ; no, virtual address space is the same, so don't reload it and cause TLB flushes 42 | mov cr3,eax ; yes, load the next task's virtual address space 43 | .doneVAS: 44 | 45 | pop ebp 46 | pop edi 47 | pop esi 48 | pop ebx 49 | 50 | ret ;Load next task's EIP from its kernel stack 51 | -------------------------------------------------------------------------------- /Source/drivers/pci/deviceNames.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - List of PCI device names. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include "deviceNames.h" 8 | #include 9 | #include 10 | #include 11 | 12 | struct pci_deviceName { 13 | uint16 vendor; 14 | uint16 device; 15 | char* name; 16 | deviceTree_Entry* (* initialisationFunction)(pciBus_Entry*); 17 | }; 18 | 19 | const struct pci_deviceName deviceNames[] = { 20 | { 0x8086, 0x100E, "82540EM Gigabit Ethernet Controller", NULL }, 21 | { 0x8086, 0x1237, "i440FX PCI Host Bridge", NULL }, 22 | 23 | { 0x8086, 0x7000, "PIIX3 PCI-to-ISA Bridge", NULL }, 24 | { 0x8086, 0x7010, "PIIX3 IDE Controller", NULL }, 25 | { 0x8086, 0x7020, "PIIX3 UHCI USB Controller", NULL }, 26 | 27 | { 0x8086, 0x7110, "PIIX4 PCI-to-ISA Bridge", NULL }, 28 | { 0x8086, 0x7111, "PIIX4 IDE Controller", NULL }, 29 | { 0x8086, 0x7112, "PIIX4 UHCI USB Controller", NULL }, 30 | { 0x8086, 0x7113, "PIIX4 ACPI", NULL }, 31 | 32 | { 0x8086, 0x7192, "i440BX/ZX/DX PCI Host Bridge", NULL }, 33 | 34 | { 0x10EC, 0x8139, "Realtek 8139 Network Adapter", NULL }, 35 | 36 | { 0x1000, 0x0012, "LSI 53C895A SCSI Controller", NULL }, 37 | { 0x1000, 0x0060, "LSI MegaRAID 1078 SAS Controller", NULL }, 38 | 39 | { 0x1002, 0x5046, "ATI Rage 128 Pro Display Adapter", NULL }, 40 | 41 | { 0x1234, 0x1111, "QEMU Standard VGA Graphics Adapter", qemuVga_initialise }, 42 | { 0x1B36, 0x0011, "QEMU PVPANIC Device", NULL }, 43 | 44 | { 0x1414, 0x5353, "Microsoft Hyper-V Graphics Adapter", NULL }, 45 | 46 | // The last entry in the table is a dummy entry to mark the end of the table. 47 | { 0xFFFF, 0xFFFF, "DUMMY ENTRY - END OF TABLE", NULL } 48 | }; 49 | 50 | char* pci_getNameFromVendorAndDevice(uint16 vendor, uint16 device) { 51 | struct pci_deviceName* deviceNameEntry = deviceNames; 52 | 53 | while (deviceNameEntry->vendor != 0xFFFF && deviceNameEntry->device != 0xFFFF) { 54 | if (deviceNameEntry->vendor == vendor && deviceNameEntry->device == device) { 55 | return deviceNameEntry->name; 56 | } 57 | 58 | deviceNameEntry++; 59 | } 60 | 61 | return "Unknown PCI Device"; 62 | } 63 | -------------------------------------------------------------------------------- /Source/Includes/Drivers/atiRage128.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Driver for ATI Rage 128 Series Graphics Cards. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | * 6 | * 2D Capabilities: 7 | * [X] 2D Framebuffer 8 | * [-] Double Buffering 9 | * [-] Hardware Sprites (Cursor) 10 | * [-] Hardware Copy and Fill (Blitting) 11 | */ 12 | 13 | #include 14 | 15 | #ifndef INCLUDES_ATIRAGE128_H_ 16 | #define INCLUDES_ATIRAGE128_H_ 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | deviceTree_Entry* atiRage128_initialise(pciBus_Entry* pciDetails); 27 | 28 | /** 29 | * Contains all the information needed to set a video mode on most display adapters. 30 | */ 31 | typedef struct { 32 | char* name; 33 | 34 | /// @brief Number of horizontal pixels (columns) visible on the display. 35 | uint16 hRes; 36 | 37 | /// @brief Number of vertical pixels (rows/lines) visible on the display. 38 | uint16 vRes; 39 | 40 | /// @brief Number of pixels before the horizontal sync. 41 | uint16 hFront; 42 | 43 | /// @brief Pixel duration of the horizontal sync. 44 | uint16 hSync; 45 | 46 | /// @brief Number of pixels after the horizontal sync. 47 | uint16 hBack; 48 | 49 | /// @brief Horizontal Sync Polarity. 1 is positive. 50 | bool hSyncPolarity; 51 | 52 | /// @brief Number of lines before the vertical sync. 53 | uint16 vFront; 54 | 55 | /// @brief Duration of the vertical sync in lines. 56 | uint16 vSync; 57 | 58 | /// @brief Number of lines after the vertical sync. 59 | uint16 vBack; 60 | 61 | /// @brief Vertical sync polarity. 1 is positive. 62 | bool vSyncPolarity; 63 | 64 | /// @brief Pixel clock in Hz. 65 | uint32 pixelClock; 66 | 67 | /// @brief Number of bits per pixel. 68 | /// Currently supported values are 15 (16-bit access, 5 bits each for RGB with top bit zero) and 32 (32-bit access, 8 bits each for RGB with top byte zero). 69 | uint8 depth; 70 | } VideoMode; 71 | 72 | void atiRage128_setMode(VideoMode* mode); 73 | 74 | Canvas* atiRage128_getCanvas(); 75 | 76 | void atiRage128_dumpCursorPos(); 77 | 78 | //void atiRagePro_setMode(uint16 width, uint16 height); 79 | 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif 84 | 85 | #endif // INCLUDES_ATIRAGE128_H_ 86 | -------------------------------------------------------------------------------- /Source/Includes/thread.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Threading and Context Switching Code. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | * 6 | * See https://wiki.osdev.org/Brendan%27s_Multi-tasking_Tutorial 7 | */ 8 | 9 | #ifndef INCLUDES_THREAD_H_ 10 | #define INCLUDES_THREAD_H_ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | 18 | /** 19 | * The data stored in the kernel about a thread, including information needed for context switching, time accounting, etc. 20 | */ 21 | typedef struct thread_control_block_S { 22 | // The location of the first two elements in this structure (CR3 and ESP) is critical, as they are referenced by offset from taskswitch.asm. 23 | 24 | /** 25 | * TODO(JackScottAU): Rename this to something processor agnostic, like "KernelStackPointer". 26 | */ 27 | void* kernel_stack_top; 28 | 29 | /** 30 | * The value of the CR3 register for this thread. This should be a pointer to the start of the thread's page directory. 31 | * In an ideal world, this would be in the process control block. However, we put it here as it is much easier to get to. 32 | * 33 | * TODO(JackScottAU): Rename this to something processor agnostic, like "PageDirectoryPointer" or maybe "PageControlPointer". 34 | */ 35 | uint32 cr3; 36 | 37 | // TODO: add the kernel stack bottom (highest address) here, to be used switching from user to supervisor mode. 38 | 39 | // TODO: describe this. 40 | void* process_control_block; 41 | 42 | /** 43 | * The next thread in the queue. 44 | */ 45 | struct thread_control_block_S* nextThread; 46 | // char threadName[16]; // 16 characters to store the threads name in? 47 | } thread_control_block; 48 | 49 | thread_control_block* initialise_multitasking(); // current task ends up current_task_tcb 50 | void switch_to_task(thread_control_block* next_thread); 51 | thread_control_block* new_task(void (* callback)(), thread_control_block* currentTask); 52 | 53 | // move these to scheduler.h, make them general for the kernel, thread.h move to x86. 54 | void thread_startScheduler(); // scheduler_start() 55 | void thread_stopScheduler(); // scheduler_stop() 56 | void scheduler(); // scheduler_pickTask() 57 | 58 | // TODO: move scheduler into the non-arch area of the kernel. 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif // INCLUDES_THREAD_H_ 65 | -------------------------------------------------------------------------------- /Source/library/string.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - String manipulation functions. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void string_copy(char *dest, const char* src) { 13 | while (*src) { // while source string isnt null, copy, then increase pointer. 14 | *dest = *src; 15 | dest++; 16 | src++; 17 | } 18 | } 19 | 20 | int string_compare(const char* a, const char* b) { 21 | while (*a && *a == *b) { 22 | ++a; ++b; 23 | } 24 | 25 | return (int)(unsigned char)(*a) - (int)(unsigned char)(*b); 26 | } 27 | 28 | void string_toLower(char* string) { 29 | while (*string) { 30 | if (*string >= 0x41 && *string <= 0x5A) { 31 | *string += 0x20; 32 | } 33 | string++; 34 | } 35 | } 36 | 37 | char** string_split(char* string, char splitter) { 38 | // to store pointers. TODO: count splitter occurences beforehand so we allocate the right amount. 39 | char** strings = memoryManager_allocate(sizeof(char*) * 128); 40 | 41 | int i = 0; 42 | char* stringStart = string; 43 | 44 | while (*string != '\0') { 45 | // debug(LOGLEVEL_DEBUG, "Remaining text: %s", string); 46 | 47 | if (*string == splitter || *string == '\0') { 48 | // debug(LOGLEVEL_DEBUG, "Match"); 49 | strings[i] = stringStart; 50 | *string = '\0'; 51 | stringStart = string + 1; 52 | i++; 53 | } 54 | 55 | string++; 56 | } 57 | 58 | strings[i] = stringStart; 59 | strings[i + 1] = NULL; 60 | 61 | return strings; 62 | } 63 | 64 | uint32 string_length(const char* string) { 65 | uint32 len = 0; 66 | 67 | while (*string) { 68 | len++; 69 | string++; 70 | } 71 | 72 | return len; 73 | } 74 | 75 | void string_toUpper(char* string) { 76 | while (*string) { 77 | if (*string >= 0x61 && *string <= 0x6A) { 78 | *string -= 0x20; 79 | } 80 | string++; 81 | } 82 | } 83 | 84 | uint32 string_parseInt(const char* string) { 85 | int result = 0; 86 | 87 | for (int i = 0; string[i] <= '9' && string[i] >= '0'; ++i) { 88 | result = result * 10 + string[i] - '0'; 89 | } 90 | 91 | return result; 92 | } 93 | -------------------------------------------------------------------------------- /Source/library/fifobuffer.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Generic FIFO Buffer. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | FIFOBuffer* FIFOBuffer_new(uint32 size) { 12 | FIFOBuffer* buffer = memoryManager_allocate(sizeof(FIFOBuffer)); 13 | 14 | buffer->buffer = memoryManager_allocate(size); 15 | buffer->size = size; 16 | buffer->readIndex = 0; 17 | buffer->writeIndex = 0; 18 | 19 | return buffer; 20 | } 21 | 22 | uint32 FIFOBuffer_WriteBytes(FIFOBuffer* buffer, uint8* data, uint32 size) { 23 | uint32 i; 24 | const uint8 * p; 25 | p = data; 26 | for (i = 0; i < size; i++) { 27 | // first check to see if there is space in the buffer 28 | if ( (buffer->writeIndex + 1 == buffer->readIndex) || 29 | ( (buffer->writeIndex + 1 == buffer->size) && (buffer->readIndex == 0) )) { 30 | return i; // no more room 31 | } else { 32 | buffer->buffer[buffer->writeIndex] = *p++; 33 | buffer->writeIndex++; // increment the head 34 | 35 | if ( buffer->writeIndex == buffer->size ) { // check for wrap-around 36 | buffer->writeIndex = 0; 37 | } 38 | } 39 | } 40 | 41 | return size; 42 | } 43 | 44 | uint32 FIFOBuffer_ReadBytes(FIFOBuffer* buffer, uint8* data, uint32 size) { 45 | uint32 i; 46 | uint8 * p; 47 | p = data; 48 | for (i = 0; i < size; i++) { 49 | if ( buffer->readIndex != buffer->writeIndex ) { // see if any data is available 50 | *p++ = buffer->buffer[buffer->readIndex]; // grab a byte from the buffer 51 | buffer->readIndex++; // increment the tail 52 | if (buffer->readIndex == buffer->size) { // check for wrap-around 53 | buffer->readIndex = 0; 54 | } 55 | } else { 56 | return i; // number of bytes read 57 | } 58 | } 59 | 60 | return size; 61 | } 62 | 63 | uint32 FIFOBuffer_ContentsSize(FIFOBuffer* buffer) { 64 | if (buffer->readIndex == buffer->writeIndex) { 65 | return 0; 66 | } 67 | 68 | if (buffer->readIndex > buffer->writeIndex) { 69 | return buffer->readIndex - buffer->writeIndex; 70 | } 71 | 72 | // TODO(JackScottAU): Pretty sure there's a bug here. Will (might) fix later. 73 | return buffer->writeIndex - buffer->readIndex; 74 | } 75 | -------------------------------------------------------------------------------- /Resources/Fonts/Tamsyn/README: -------------------------------------------------------------------------------- 1 | _____ 2 | |_ _|_ _ _ __ ___ ___ _ _ _ __ 3 | | |/ _` | '_ ` _ \/ __| | | | '_ \ 4 | | | (_| | | | | | \__ \ |_| | | | | 5 | |_|\__,_|_| |_| |_|___/\__, |_| |_| 6 | |___/ 7 | 8 | Monospaced Programming Font for Linux and Windows 9 | -------------------------------------------------------------------------------- 10 | 11 | VERSION 1.11 2015-01-21 12 | 13 | CHANGES IN 1.11 14 | 15 | Changed capital "A" to be less pointy, more consistent between sizes. 16 | Expecting hate mail. 17 | 18 | Previously, unimplemented chars defaulted to print a blank space under X. 19 | Now those chars are not placed in the font at all in order to allow 20 | fontconfig to fall back to use a char from another font. (Thanks, Allen, 21 | for pointing this out and testing the fix.) 22 | 23 | CHANGES IN 1.10 24 | 25 | New size: 7x13. 26 | 27 | New size: 8x16 (replaces 8x17) 28 | 29 | Many changes in favor of a cleaner look. In particular, replaced the 30 | double-story lowercase "g" with the single-story variety. 31 | 32 | Added the degree symbol. (Enjoy, Sven!) 33 | 34 | Added the copyright symbol. 35 | 36 | CHANGES IN 1.9 37 | 38 | Fixed the problem with inverted question mark facing the wrong way. (Thanks 39 | to Swyter for reporting it.) 40 | 41 | Modified the small "w" and "g". 42 | 43 | Restored the original look for size 6x12 bold. 44 | 45 | CHANGES IN 1.8 46 | 47 | Fixed a problem where chars showed up as boxes in xfontsel and some apps. 48 | 49 | Tweaks, esp. to 8x17 size 50 | 51 | CHANGES IN 1.7 52 | 53 | Added two new sizes: tiny 5x9 and large 10x20. 54 | 55 | Retired size 7x12. (If this presents major problem for you, get in touch.) 56 | 57 | Added a pixel to the height of the 6x11 size, making it 6x12. 58 | 59 | Added new chars for partial ISO8859-1 support, mainly covering the accented 60 | characters and currency symbols. I consider these new characters to be BETA, 61 | since I don't use them so I'm not sure how they should look. If you have 62 | feedback about them, please get in touch. 63 | 64 | Made many minor tweaks to character shapes. In particular, the f, l, and t 65 | have a more "traditional" look. Also, I've changed the w and y again. 66 | 67 | FUTURE PLANS: 68 | 69 | Full CP437, ISO8859-1, Windows-1252 coverage 70 | 71 | Mac/ttf version 72 | 73 | Cyrillic 74 | 75 | Unified FON file containing all sizes in regular and bold 76 | 77 | Separate OEM and 1252 Windows versions 78 | 79 | 80 | User feedback is welcomed and encouraged. 81 | 82 | Enjoy! 83 | -------------------------------------------------------------------------------- /Docs/Platforms.md: -------------------------------------------------------------------------------- 1 | # Platforms 2 | 3 | One of Amethyst's goals is to run on as many different computer hardware platforms as possible. 4 | 5 | Want to support both x86 and x64, arm in various forms, plus risc-v once it gets cheap, plus powerpc and 68k for old school kool. 6 | 7 | These all have a corresponding #define, and when that define is set via makefile it compiles the kernel for that architecture. 8 | 9 | All archs are a seven-letter code. Last two is bits. 10 | 11 | | Priority | Platform Code | Description | 12 | | -------- | ------------- | ----------------------------------------------------------------------------------------- | 13 | | Tier 1 | `Intel32` | 32-bit x86 BIOS PC (PC-97) | 14 | | Tier 1 | `Intel64` | 64-bit x86 UEFI PC | 15 | | Tier 1 | `ArmPi32` | 1st and 2nd Generation Raspberry Pi Boards | 16 | | Tier 2 | `ArmPi64` | 3rd and Subsequent Generation Raspberry Pi Boards | 17 | | Tier 2 | `ArmPc64` | Arm PC Base System Architecture 1.0 | 18 | | Tier 2 | `Power64` | 5th Generation PowerPC-based Macintoshes | 19 | | Tier 3 | `Power32` | 1st to 4th Generation PowerPC-based Macintoshes | 20 | | Tier 3 | `Motor32` | Motorola 68000-based Macintoshes | 21 | 22 | Other Potential Platforms: 23 | 24 | - Loong64 25 | - Riscv64 (BRS defines the system) 26 | - Itanium?! 27 | - Sparc32 28 | - Sparc64 29 | - PDP-11? (16-bit) 30 | - IBM Series/1 (16-bit) 31 | - IBM System/36 (16-bit) 32 | - VAX 33 | - MIPS stuff 34 | 35 | ## Intel32 36 | 37 | Follows the PC-97 Standard. A 586 machine with a PCI bus. 38 | 39 | CPU choice: 586 as that supports CPUID and multi-processor and FPU. 686 adds PAE. 686 *might* add MMX, but not necessarily. 486 doesn't have CPUID. 40 | 41 | ### Booting 42 | 43 | Booting is always done via multiboot. This gives us several options: 44 | 45 | - Using GRUB on hard disk. 46 | - Using GRUB on CD-ROM. 47 | - Loading via iPXE either using CD-ROM or Network card ROM image. 48 | 49 | ## Intel64 50 | 51 | uefi pc (noting a gap in systems that are 64-bit but not uefi, this is a hole we can afford to lose, it's about 5 years from 2010 to 2015) 52 | -------------------------------------------------------------------------------- /Source/cpuid.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - CPUID helper class. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | CPUID::CPUID() { 13 | unsigned int eaxvar = 0, ebxvar = 0, ecxvar = 0, edxvar = 0; 14 | __get_cpuid(0, &eaxvar, &ebxvar, &ecxvar, &edxvar); 15 | 16 | maxLevel = eaxvar; 17 | } 18 | 19 | uint8* CPUID::getManufacturerString() { 20 | uint8* string = (uint8*) memoryManager_allocate(13); 21 | string[12] = 0; // null terminate. 22 | 23 | unsigned int eaxvar = 0, ebxvar = 0, ecxvar = 0, edxvar = 0; 24 | __get_cpuid(0, &eaxvar, &ebxvar, &ecxvar, &edxvar); 25 | 26 | string[0] = ((uint8*)&ebxvar)[0]; 27 | string[1] = ((uint8*)&ebxvar)[1]; 28 | string[2] = ((uint8*)&ebxvar)[2]; 29 | string[3] = ((uint8*)&ebxvar)[3]; 30 | 31 | string[4] = ((uint8*)&edxvar)[0]; 32 | string[5] = ((uint8*)&edxvar)[1]; 33 | string[6] = ((uint8*)&edxvar)[2]; 34 | string[7] = ((uint8*)&edxvar)[3]; 35 | 36 | string[8] = ((uint8*)&ecxvar)[0]; 37 | string[9] = ((uint8*)&ecxvar)[1]; 38 | string[10] = ((uint8*)&ecxvar)[2]; 39 | string[11] = ((uint8*)&ecxvar)[3]; 40 | 41 | return string; 42 | } 43 | 44 | uint8 CPUID::getFamily() { 45 | if (maxLevel < 1) 46 | return 0; // we don't have exception support yet. 47 | 48 | unsigned int eaxvar = 0, ebxvar = 0, ecxvar = 0, edxvar = 0; 49 | __get_cpuid(1, &eaxvar, &ebxvar, &ecxvar, &edxvar); 50 | 51 | uint32 family = (eaxvar >> 8) & 0xF; 52 | uint32 extendedFamily = (eaxvar >> 20) & 0xFF; 53 | 54 | if (family == 15) { 55 | return family + extendedFamily; 56 | } else { 57 | return family; 58 | } 59 | } 60 | 61 | uint8 CPUID::getModel() { 62 | if (maxLevel < 1) 63 | return 0; // we don't have exception support yet. 64 | 65 | unsigned int eaxvar = 0, ebxvar = 0, ecxvar = 0, edxvar = 0; 66 | __get_cpuid(1, &eaxvar, &ebxvar, &ecxvar, &edxvar); 67 | 68 | uint32 family = (eaxvar >> 8) & 0xF; 69 | uint32 model = (eaxvar >> 4) & 0xF; 70 | uint32 extendedModel = (eaxvar >> 16) & 0xF; 71 | 72 | if (family == 6 || family == 15) { 73 | return model + (extendedModel << 4); 74 | } else { 75 | return model; 76 | } 77 | } 78 | 79 | uint8 CPUID::getStepping() { 80 | if (maxLevel < 1) 81 | return 0; // we don't have exception support yet. 82 | 83 | unsigned int eaxvar = 0, ebxvar = 0, ecxvar = 0, edxvar = 0; 84 | __get_cpuid(1, &eaxvar, &ebxvar, &ecxvar, &edxvar); 85 | 86 | return eaxvar & 0x0F; 87 | } 88 | -------------------------------------------------------------------------------- /Source/Includes/Clock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Time/Date and Timer Functions. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_CLOCK_H_ 8 | #define INCLUDES_CLOCK_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** 17 | * This is the representation of time used throughout the entire 18 | * operating system. It allows for approximately +/- 292.5 billion years 19 | * from our epoch (2000-01-01 00:00:00.000) with a precision of one 20 | * millisecond (set in CLOCK_HERTZ below). 21 | * 22 | * The milliSeconds field is always positive, like so: 23 | * seconds=-1, milliSeconds=1 == 1999-12-31 23:59:59.001, NOT 24 | * 1999-12-31 23:59:59.999 !!! 25 | */ 26 | typedef struct { 27 | sint64 seconds; 28 | uint16 milliSeconds; 29 | } time_t; 30 | 31 | typedef struct clock_timerRequest_s { 32 | sint64 seconds; 33 | uint16 milliSeconds; 34 | void (* funcToCall)(void); 35 | uint32 isRepeatTimer; 36 | uint64 repeatSecondsToAdd; 37 | uint16 repeatMilliSecondsToAdd; 38 | struct clock_timerRequest_s* next; 39 | } clock_timerRequest; 40 | 41 | void clock_init(void); 42 | void clock_shutdown(void); 43 | void clock_handler_PIC(uint32 arbitraryNumber); 44 | uint32 clock_uptime(void); 45 | 46 | /** 47 | * Sets the divisor of the PIT channel 0 clock. 48 | * @param hertz Requested frequency. 49 | */ 50 | void clock_setHertz(unsigned int hertz); 51 | 52 | uint8 clock_getRTCRegister(uint8 chosenRegister); 53 | void clock_updateTimeFromRTC(time_t* clock); 54 | uint8 clock_convertBCDtoNormal(uint8 value); 55 | 56 | clock_timerRequest* clock_addOneShotRequest(time_t* requestedTime, void (*funcToCall)(void)); 57 | clock_timerRequest* clock_addRepeatRequest(uint64 secGap, uint16 milGap, void (*funcToCall)(void)); 58 | void clock_deleteTimerRequest(clock_timerRequest* request); 59 | 60 | #define CLOCK_HERTZ 1000 61 | 62 | #define SECONDS_PER_MINUTE 60 63 | #define SECONDS_PER_HOUR 3600 64 | #define SECONDS_PER_DAY 86400 65 | #define SECONDS_PER_YEAR 31556926 // According to Google. We ignore leap seconds in this approximate fix. 66 | 67 | // If the RTC is in local time, this is the number that needs to be added in order to get UTC. 68 | // Example: I am in UTC+11, so my value is -11 (11-11=0) 69 | #define CLOCK_UTC_OFFSET 0 70 | 71 | // EPOCH: 2000-01-01 00:00:00.000 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif // INCLUDES_CLOCK_H_ 78 | -------------------------------------------------------------------------------- /Docs/devices-and-drivers.md: -------------------------------------------------------------------------------- 1 | A 'device' is a node in a tree that can either implement a class of device (video, sound, input, network, file, etc) or have child devices (such as be a PCI bus). 2 | 3 | ## Sample tree: 4 | 5 | x86 PC (platform driver) 6 | +- PCI Bus (bus driver) 7 | | +- Video Card (bus driver, class driver for video) 8 | | | +- Monitor #1 (class driver for monitor) 9 | | | +- Monitor #2 (class driver for monitor) 10 | | +- Sound Card (class driver for sound) 11 | | +- SATA Interface 12 | | | +- Hard Drive (class driver for raw storage) 13 | +- Programmable Interrupt Timer (class driver for clock) 14 | +- PS/2 Controller (bus driver) 15 | | +- PS/2 Keyboard (class driver for input) 16 | | +- PS/2 Mouse (class driver for input) 17 | +- PCI Root Hub - i440FX Northbridge (0:0:0) 18 | | +- PCI Bridge - PIIX3 Southbridge (0:1:0) // if slots have multiple functions, first one is a tree parent. 19 | | | +- ISA Controller (0:1:1) 20 | | | +- PCI Bridge (0:1:2) // i think this is the USB controller. 21 | | | +- ISA Controller (0:1:3) 22 | 23 | ## Driver Ideas 24 | 25 | - Don't bother implementing ISA DMA - just use port I/O for everything non-PCI. Since we are targeting 686 and above, we can assume that most devices that need fastness will be on PCI. 26 | 27 | ## Resources 28 | 29 | Every item in the tree can have resources attached to it. Resources are of type I/O, memory, or IRQ. 30 | 31 | ## Bus Devices & Drivers 32 | 33 | ### Platform Devices 34 | 35 | Each platform has one root platform device, such as "x86-64 PC", and that root device is a bus driver which finds and initialises other bus and device drivers. That way SoC can be accomodated. On x86 it would do something like try to initialise ACPI, and if that fails, try to manually init the PCI bus. 36 | 37 | The platform driver lives in the architecture-specific folder. All other devices live in the devices folder. A platform driver is simply a bus driver. 38 | 39 | ## Node Devices & Drivers 40 | 41 | ## Filter Devices 42 | 43 | For example, turns scancodes into ascii keys. 44 | 45 | ## Device Classes 46 | 47 | ## Video driver API 48 | 49 | init() 50 | getFeatures() > FEATURE_CURSOR | FEATURE_DOUBLEBUFFERING| FEATURE_etc 51 | getModes() - list in order, most preferred first (so most software can just set the first mode) 52 | setMode(mode) 53 | getCanvas() -> object has links to methods to draw cursor, switch buffers, etc 54 | 55 | ## PCI 56 | 57 | Create a sort-of hash table of drivers. Key is PCI class, sub key is subclass, then have a linked list of drivers by vendor/model IDs. The final driver in the list is a generic driver for that class/subclass of PCI device. If no drivers exist even generic one, then we fall back to an "Unknown PCI Device" driver. 58 | -------------------------------------------------------------------------------- /Source/Includes/multiboot.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Multiboot v1 Information Structure. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_MULTIBOOT_H_ 8 | #define INCLUDES_MULTIBOOT_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #define MULTIBOOT_MAGIC_NUMBER 0x2BADB002 17 | 18 | typedef enum : uint8 { 19 | IndexedColour = 0, 20 | DirectColour = 1, 21 | EGA = 2, 22 | } multiboot_framebufferType; 23 | 24 | struct multiboot_info { 25 | uint32 flags; 26 | uint32 memoryLower; 27 | uint32 memoryUpper; 28 | uint32 bootDevice; 29 | char* commandLine; 30 | uint32 modsCount; 31 | struct multiboot_moduleNode* modsAddr; 32 | uint32 syms1; 33 | uint32 syms2; 34 | uint32 syms3; 35 | uint32 syms4; 36 | uint32 memoryMapLength; 37 | struct multiboot_memoryMapNode* memoryMapAddress; 38 | 39 | uint32 drives_length; 40 | uint32 drives_addr; 41 | 42 | uint32 config_table; 43 | 44 | uint32 boot_loader_name; 45 | 46 | uint32 apm_table; 47 | 48 | uint32 vbe_control_info; 49 | uint32 vbe_mode_info; 50 | uint16 vbe_mode; 51 | uint16 vbe_interface_seg; 52 | uint16 vbe_interface_off; 53 | uint16 vbe_interface_len; 54 | 55 | void* framebuffer_addr; 56 | uint32 framebuffer_addr_upper; 57 | uint32 framebuffer_pitch; 58 | uint32 framebuffer_width; 59 | uint32 framebuffer_height; 60 | uint8 framebuffer_bpp; 61 | 62 | multiboot_framebufferType framebuffer_type; 63 | //uint8 framebuffer_type; // 0 = indexed colour, 1 = direct colour, 2 = ega text 64 | 65 | uint8 framebuffer_red_field_position; 66 | uint8 framebuffer_red_mask_size; 67 | uint8 framebuffer_green_field_position; 68 | uint8 framebuffer_green_mask_size; 69 | uint8 framebuffer_blue_field_position; 70 | uint8 framebuffer_blue_mask_size; 71 | }; 72 | 73 | /// @brief Types of memory, provided by GRUB. 74 | typedef enum : uint32 { 75 | Available = 1, 76 | Reserved = 2, 77 | ACPI = 3, 78 | 79 | /// @brief reserved memory which needs to be preserved on hibernation 80 | Hibernation = 4, 81 | 82 | /// @brief memory which is occupied by defective RAM modules 83 | Defective = 5, 84 | } multiboot_memoryType; 85 | 86 | struct multiboot_memoryMapNode { 87 | /// @brief Size of this memory map node entry. Nothing to do with the actual memory itself. 88 | uint32 size; 89 | 90 | /// @brief Start address of the memory. 91 | uint64 addr; 92 | 93 | /// @brief Length of the memory. 94 | uint64 len; 95 | 96 | /// @brief Type of the memory. 97 | multiboot_memoryType type; 98 | } __attribute__((packed)); 99 | 100 | struct multiboot_moduleNode { 101 | void* start; 102 | void* end; 103 | char* fileName; 104 | uint32 reserved; 105 | } __attribute__((packed)); 106 | 107 | #ifdef __cplusplus 108 | } 109 | #endif 110 | 111 | #endif // INCLUDES_MULTIBOOT_H_ 112 | 113 | -------------------------------------------------------------------------------- /Docs/Memory Management.md: -------------------------------------------------------------------------------- 1 | # Memory Management 2 | 3 | ## Physical Memory Layout 4 | 5 | The first four megabytes (one page table) are always mapped 1:1 into kernel space. 6 | 7 | | Start (P) | End (P) | Size | Usage | 8 | | ---------- | ---------- | -------- | ------------------------------------------------------------------------------- | 9 | | 0x00000000 | 0x0007FFFF | 512 KiB | Kernel Essential Data Structures (IDT, GDT, TSS, etc) 10 | | 0x00080000 | 0x000FFFFF | 512 KiB | Hardware Reserved (EBDA, BIOS ROM, VGA ROM, VGA RAM, etc) 11 | | 0x00100000 | 0x003FFFFF | 3 MiB | Kernel Code and Modules, loaded by the Multiboot bootloader 12 | 13 | After that, the kernel retrieves the memory map from the bootloader (GRUB) and also from the PCI configuration space to find out what is usable memory and what is reserved. 14 | 15 | ### Kernel Data Structures 16 | 17 | | Start (P) | End (P) | Size | Usage | 18 | | ---------- | ---------- | -------- | ------------------------------------------------------------------------------- | 19 | | 0x00002000 | 0x00002071 | 72 B | Task State Segment Structure | 20 | | 0x00060000 | 0x0007FFFF | 128 KiB | Physical Memory Allocation Bitmap | 21 | 22 | We also want to put the IDT, GDT, etc in here 23 | 24 | ### Physical Memory Allocation Bitmap 25 | 26 | 128K is 1024x1024 / 8. 27 | 28 | All zeroes = all allocated. 29 | 30 | All ones = all free. 31 | 32 | ## Virtual Memory Layout 33 | 34 | 0GB to 3GB is user space. 35 | 3GB to 4GB is kernel space (and is the same in every process). 36 | 37 | | Start (V) | End (V) | Size | Usage | 38 | | ---------- | ---------- | -------- | ------------------------------------------------------------------------------- | 39 | | 0x00000000 | 40 | 41 | ### Kernel Space 42 | 43 | Kernel space is 256MiB from each address space on x86. 0xF0000000 upwards. 44 | 45 | - V0xC0000000 upwards = kernel code and modules and heap 46 | 47 | - 48 | 49 | Remember each kernel process has it's own space. So we need lots of space for kernel stacks (if we reserved 4MB, that's 1024 threads per process which should be enough). 50 | 51 | | Start (V) | End (V) | Size | Usage | 52 | | ---------- | ---------- | -------- | ------------------------------------------------------------------------------- | 53 | | 0xF0000000 | 0xF03FFFFF | 4 MiB | Kernel Code + Kernel Core Data Structures | 54 | 55 | | 0xF1000000 | 0xFEFFFFFF | 224 MiB | Kernel-Mode Heap Space | 56 | 57 | | 0xFF800000 | 0xFFBFFFFF | 4 MiB | Kernel Stacks (4KiB per Thread) | < this is the only part of the kernel directory which changes per process 58 | | 0xFFC00000 | 0xFFFFFFFF | 4 MiB | Self-Reference to Page Directory | 59 | 60 | ### Boot Page Directory 61 | 62 | The first page directory loaded during boot maps 0MB to 4MB physical to both: 63 | 0MiB to 4MiB virtual 64 | 3GiB to 3.004GiB virtual 65 | 66 | As soon as paging is set up it then removes the identity mapping. -------------------------------------------------------------------------------- /Source/drivers/mouse.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - PC Mouse Driver. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | typedef struct { 20 | sint16 x; 21 | sint16 y; 22 | } MouseEvent; 23 | 24 | MouseEvent mouseEvent; 25 | 26 | uint8 currentMouseByte; 27 | uint8 numberOfMouseBytes; 28 | 29 | uint8 mouseBytes[4]; 30 | 31 | bool leftDown; 32 | 33 | void mouse_interruptHandler(uint32 eventData); 34 | 35 | void mouse_interruptHandler(uint32 eventData) { 36 | uint8 data = portIO_read8(0x60); 37 | debug(LOGLEVEL_DEBUG, "Mouse data received: %h", data); 38 | 39 | mouseBytes[currentMouseByte] = data; 40 | currentMouseByte++; 41 | 42 | if (currentMouseByte >= numberOfMouseBytes) { 43 | currentMouseByte = 0; 44 | } 45 | 46 | if (currentMouseByte == 0) { 47 | // we have a full mouse packet in the array buffer. 48 | if (mouseBytes[0] & 0x10) { 49 | mouseEvent.x = mouseBytes[1] | 0xFF00; 50 | } else { 51 | mouseEvent.x = mouseBytes[1]; 52 | } 53 | 54 | if (mouseBytes[0] & 0x20) { 55 | mouseEvent.y = mouseBytes[2] | 0xFF00; 56 | } else { 57 | mouseEvent.y = mouseBytes[2]; 58 | } 59 | 60 | debug(LOGLEVEL_INFO, "Mouse X: %d, Mouse Y: %d", mouseEvent.x, mouseEvent.y); 61 | 62 | // The intermediary that sends the mouse event to the GUI. 63 | sortOfMouse_HandleEvent(mouseEvent.x, mouseEvent.y * -1); 64 | 65 | bool leftNewDown = mouseBytes[0] & 0x01; 66 | 67 | if (leftDown && !leftNewDown) { 68 | // Mouse up 69 | sortOfMouse_HandleClickEvent(); 70 | } 71 | 72 | if (!leftDown && leftNewDown) { 73 | // mouse down 74 | } 75 | 76 | leftDown = leftNewDown; 77 | } 78 | } 79 | 80 | deviceTree_Entry* mouse_initialise() { 81 | interrupts_addHandler(0x2C, 0, (*mouse_interruptHandler)); 82 | 83 | // enable mouse data sending 84 | debug(LOGLEVEL_DEBUG, "PS/2 Mouse: Sending reset..."); 85 | ps2controller_sendByteToDevice(2, 0xFF); // RESET. 86 | ps2controller_receiveByteFromDevice(2); 87 | ps2controller_receiveByteFromDevice(2); 88 | ps2controller_receiveByteFromDevice(2); 89 | 90 | debug(LOGLEVEL_DEBUG, "PS/2 Mouse: Setting sample rate..."); 91 | ps2controller_sendByteToDevice(2, 0xF3); // set rate. 92 | ps2controller_receiveByteFromDevice(2); 93 | ps2controller_sendByteToDevice(2, 200); // 100 samples per second. 94 | ps2controller_receiveByteFromDevice(2); 95 | 96 | debug(LOGLEVEL_DEBUG, "PS/2 Mouse: Enabling data sending..."); 97 | ps2controller_sendByteToDevice(2, 0xF4); // enable data 98 | ps2controller_receiveByteFromDevice(2); 99 | 100 | currentMouseByte = 0; 101 | numberOfMouseBytes = 3; 102 | leftDown = false; 103 | 104 | return deviceTree_createDevice("Generic PS/2 Mouse", DEVICETREE_TYPE_OTHER, NULL); 105 | } 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Amethyst Operating System Logo](/Resources/amethyst-logotext.png) 2 | 3 | # Amethyst Operating System 4 | 5 | ## Introduction 6 | 7 | This is my operating system hobby project. 8 | 9 | The goal, as much as there is one, is to produce an operating system vaguely 10 | similar to Windows 95/NT4 (if Windows 95 didn't inherit baggage from DOS and 3.1). 11 | A feature set might include: 12 | 13 | * Shell and Scripting Language 14 | * Support for FAT32/ISO9660/EXT2 filesystems on IDE/SCSI/USB devices 15 | * GUI Windowing System w. UI Toolkit 16 | * TCP/IP Networking Stack 17 | * GUI User-Mode Applications: 18 | * Text Editor 19 | * Terminal Emulator 20 | * File Browser 21 | * Calculator 22 | 23 | The system is currently being developed for x86-based PCs, but additional 24 | platforms will be supported eventually. Eventual platforms will include: 25 | 26 | * 32-bit PCs (Architecture 'x86-32') - primary focus at the moment 27 | * 64-bit PCs (Architecture 'x86-64') 28 | * Raspberry Pi 5 (Architecture 'rpi-5') 29 | 30 | This repository contains the kernel and device drivers, and is written in C/C++ with scatterings 31 | of architecture-specific assembly. The user-mode applications will be in another repository when the time comes. 32 | 33 | ![Screenshot of Amethyst Running in QEMU](/Resources/screenshot.png) 34 | 35 | ## Supported Platforms 36 | 37 | ### x86-32 Architecture 38 | 39 | The 'x86-32' architecture attempts to follow the [PC-97 design standard](https://www.tech-insider.org/windows/research/1997/0711.html) for x86-based IBM-compatible PCs. 40 | 41 | * Intel Pentium CPU (or later/equivalent) 42 | * BIOS Boot Services 43 | * 16MiB RAM 44 | * CD-ROM drive 45 | * Keyboard 46 | * PCI Bus 47 | * QEMU graphics adapter (good luck on real hardware for now...) 48 | 49 | Testing is done via Hyper-V, VirtualBox, QEMU, etc. Initial driver efforts will be focused on i440 northbridges and PIIX1/3/4 southbridges. 50 | 51 | ### Future Platforms 52 | 53 | Future work may include support for the following platforms: 54 | 55 | * **x86-64** - UEFI-based x64 PC 56 | * **rpi-5** - Raspberry Pi 5 57 | 58 | ## Compilation & Installation 59 | 60 | ### Build System Requirements 61 | 62 | The following packages are needed: 63 | 64 | * build-essential 65 | * grub-pc-bin 66 | * xorriso 67 | * cpplint 68 | * qemu-system-x86 69 | * nasm 70 | 71 | You will also need cross-compilers for the supported platforms: 72 | 73 | * i586-elf-gcc 74 | 75 | ### Development Cycle 76 | 77 | Once the prerequisites are met, you can run the following command to compile 78 | the operating system and launch it in the QEMU emulator: 79 | 80 | make x86_32 81 | 82 | ### Makefile Options 83 | 84 | * **make clean** - Removes all intermediate and final build output. Necessary 85 | when switching platforms. 86 | * **make lint** - Runs a code linter and informs you of the thousands of issues. 87 | * **make kernel-x86_32** - Builds the kernel for the x86_32 platform. Useful 88 | for testing compilation. 89 | * **make image-x86_32** - Builds the final installation image for the x86_32 90 | platform. 91 | * **make qemu-x86_32** - Runs the built image in QEMU. 92 | 93 | ## Further Information 94 | 95 | More documentation is in the documentation folder: 96 | 97 | * [General Notes](Docs/Notes.md) 98 | * [Devices and Drivers](Docs/devices-and-drivers.md) 99 | 100 | ## Contributing 101 | 102 | The issue tracker as well as the primary mirror for the git repository are on 103 | [GitHub](https://github.com/JackScottAU/Amethyst). 104 | 105 | Amethyst icons created by Kason Koo - Flaticon 106 | -------------------------------------------------------------------------------- /Source/Includes/Structures/linkedlist.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Linked List code. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #ifndef INCLUDES_STRUCTURES_LINKEDLIST_HPP_ 8 | #define INCLUDES_STRUCTURES_LINKEDLIST_HPP_ 9 | 10 | #include 11 | 12 | template 13 | class LinkedListItem { 14 | public: 15 | T data; 16 | LinkedListItem* next; 17 | LinkedListItem* prev; 18 | }; 19 | 20 | template 21 | class LinkedList { 22 | public: 23 | LinkedList() { 24 | head = nullptr; 25 | current = nullptr; 26 | }; 27 | 28 | /// @brief Moves to the next item, if it exists. 29 | /// @return True if there was an item to move to. False if there is no item here. 30 | bool Next() { 31 | if(current == nullptr) { 32 | return false; 33 | } 34 | 35 | current = current->next; 36 | 37 | return current != nullptr; 38 | }; 39 | 40 | /// @brief Moves to the previous item, if it exists. 41 | /// @return True if there was an item to move to. False if there is no item here. 42 | bool Previous() { 43 | if(current == nullptr) { 44 | return false; 45 | } 46 | 47 | current = current->prev; 48 | 49 | return current != nullptr; 50 | }; 51 | 52 | /// @brief Returns the data at the current location in the list. 53 | /// @return 54 | T Current() { 55 | if(current == nullptr) { 56 | return nullptr; 57 | } 58 | 59 | return current->data; 60 | } 61 | 62 | /// @brief Adds data to the end of the list. 63 | /// @param data 64 | void Add(T data); 65 | 66 | void Remove(T data); 67 | 68 | /// @brief Resets the seek position to the head of the list. 69 | void Reset(); 70 | 71 | private: 72 | LinkedListItem* head; 73 | LinkedListItem* current; 74 | }; 75 | 76 | template 77 | void LinkedList::Add(T data) { 78 | if(head == nullptr) { 79 | // special case first item in list. 80 | LinkedListItem* item = new LinkedListItem(); 81 | item->data = data; 82 | item->next = head; 83 | item->prev = nullptr; 84 | 85 | head = item; 86 | 87 | current = head; 88 | } else { 89 | // add to end 90 | LinkedListItem* ocurrent = head; 91 | 92 | while(ocurrent->next != nullptr) { 93 | ocurrent = ocurrent->next; 94 | } 95 | 96 | LinkedListItem* item = new LinkedListItem(); 97 | item->data = data; 98 | item->next = nullptr; 99 | item->prev = ocurrent; 100 | ocurrent->next = item; 101 | } 102 | } 103 | 104 | template 105 | void LinkedList::Remove(T data) { 106 | // step 1: find the right item. 107 | LinkedListItem* item; 108 | 109 | item = head; 110 | 111 | while(item->data != data && item != nullptr) { 112 | item = item->next; 113 | } 114 | 115 | //here: either we have item or null. 116 | if(item == nullptr) { 117 | return; 118 | } 119 | 120 | // remove this item. 121 | LinkedListItem* p = item->prev; 122 | LinkedListItem* n = item->next; 123 | 124 | p->next = n; 125 | n->prev = p; 126 | 127 | delete item; 128 | } 129 | 130 | template 131 | void LinkedList::Reset() { 132 | current = head; 133 | } 134 | 135 | 136 | #endif // INCLUDES_STRUCTURES_LINKEDLIST_HPP_ 137 | -------------------------------------------------------------------------------- /Source/drivers/keyboard.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - PC Keyboard Driver. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | bool shift = false; 17 | bool capsActive = false; 18 | bool commandMode = false; 19 | FIFOBuffer* keyboard_buffer; 20 | 21 | void keyboard_interruptHandler(uint32 eventData); 22 | 23 | void keyboard_interruptHandler(uint32 eventData) { 24 | uint8 data = portIO_read8(0x60); 25 | 26 | debug(LOGLEVEL_TRACE, "Keyboard data received: %h", data); 27 | 28 | if (commandMode == true) { 29 | // discard data for now. 30 | } else { 31 | FIFOBuffer_WriteBytes(keyboard_buffer, &data, 1); 32 | } 33 | } 34 | 35 | deviceTree_Entry* keyboard_initialise() { 36 | interrupts_addHandler(0x21, 0, (*keyboard_interruptHandler)); 37 | 38 | keyboard_buffer = FIFOBuffer_new(1024); 39 | 40 | shift = false; 41 | capsActive = false; 42 | commandMode = false; 43 | 44 | return deviceTree_createDevice("Generic PS/2 Keyboard", DEVICETREE_TYPE_OTHER, NULL); 45 | } 46 | 47 | // 8 scancodes per line = 16 lines. 48 | uint8 keyboard_scanCodesNormal[128] = { 49 | 0, 0, '1', '2', '3', '4', '5', '6', 50 | '7', '8', '9', '0', '-', '=', '\b', '\t', 51 | 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 52 | 'o', 'p', '[', ']', '\n', 0, 'a', 's', 53 | 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 54 | '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 55 | 'b', 'n', 'm', ',', '.', '/', 0, 0, 56 | 0, ' ', 0, 0, 0, 0, 0, 0, 57 | 0, 0, 0, 0, 0, 0, 0, 0, 58 | 0, 0, 0, 0, 0, 0, 0, 0, 59 | 0, 0, 0, 0, 0, 0, 0, 0, 60 | 0, 0, 0, 0, 0, 0, 0, 0, 61 | 0, 0, 0, 0, 0, 0, 0, 0, 62 | 0, 0, 0, 0, 0, 0, 0, 0, 63 | 0, 0, 0, 0, 0, 0, 0, 0, 64 | 0, 0, 0, 0, 0, 0, 0, 0, 65 | }; 66 | 67 | // 8 scancodes per line = 16 lines. 68 | uint8 keyboard_scanCodesShift[128] = { 69 | 0, 0, '!', '@', '#', '$', '%', '^', 70 | '&', '*', '(', ')', '_', '+', '\b', '\t', 71 | 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 72 | 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 73 | 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', 74 | '"', '~', 0, '|', 'Z', 'X', 'C', 'V', 75 | 'B', 'N', 'M', '<', '>', '?', 0, 0, 76 | 0, ' ', 0, 0, 0, 0, 0, 0, 77 | 0, 0, 0, 0, 0, 0, 0, 0, 78 | 0, 0, 0, 0, 0, 0, 0, 0, 79 | 0, 0, 0, 0, 0, 0, 0, 0, 80 | 0, 0, 0, 0, 0, 0, 0, 0, 81 | 0, 0, 0, 0, 0, 0, 0, 0, 82 | 0, 0, 0, 0, 0, 0, 0, 0, 83 | 0, 0, 0, 0, 0, 0, 0, 0, 84 | 0, 0, 0, 0, 0, 0, 0, 0, 85 | }; 86 | 87 | char keyboard_readChar(void) { 88 | while (FIFOBuffer_ContentsSize(keyboard_buffer) == 0) { } 89 | 90 | uint8 data; 91 | 92 | FIFOBuffer_ReadBytes(keyboard_buffer, &data, 1); 93 | 94 | debug(LOGLEVEL_TRACE, "Keyboard readchar: %h", data); 95 | 96 | if (data & 0x80) { 97 | // A key has been released. 98 | 99 | if (data == 0xBA) { 100 | if (capsActive == true) { 101 | capsActive = false; 102 | } else { 103 | capsActive = true; 104 | } 105 | } 106 | 107 | if (data == 0xAA || data == 0xB6) { 108 | shift = false; 109 | } 110 | 111 | // Recurse to get an actual key. 112 | return keyboard_readChar(); 113 | } 114 | 115 | if (data == 0x2A || data == 0x36) { 116 | shift = true; 117 | } 118 | 119 | char key; 120 | 121 | if (shift || capsActive) { 122 | key = keyboard_scanCodesShift[data]; 123 | } else { 124 | key = keyboard_scanCodesNormal[data]; 125 | } 126 | 127 | if (key != 0) { 128 | return key; 129 | } else { 130 | return keyboard_readChar(); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Toolchain: 2 | CC := i586-elf-gcc 3 | AS := i586-elf-gcc 4 | LD := i586-elf-gcc 5 | 6 | #Options: 7 | WARNINGS := -Wall -Wextra -pedantic -Wshadow -Wpointer-arith -Wcast-align -Wno-discarded-qualifiers -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wno-long-long -Wuninitialized -Wno-unused-parameter 8 | CFLAGS := -nostdlib -fno-builtin -nostartfiles -nodefaultlibs -I Source/Includes -std=c2x $(WARNINGS) 9 | CPPFLAGS := -nostdlib -fno-builtin -nostartfiles -nodefaultlibs -I Source/Includes -ffreestanding -O2 -Wall -Wextra -fno-exceptions -fno-rtti -include ./Source/Includes/cppsupport.hpp 10 | 11 | #Top-level targets: 12 | .DEFAULT_GOAL := x86_32 13 | .PHONY: all clean cd-image hdd-image floppy-image lint qemu x86_32 kernel-x86_32 image-x86_32 qemu-x86_32 resources 14 | 15 | clean: 16 | -@rm -rf Build 17 | -@find . -name \*.o -type f -delete 18 | -@rm -f Amethyst.iso 19 | 20 | x86_32: clean qemu-x86_32 21 | 22 | kernel-x86_32: Build/kernel32 23 | 24 | image-x86_32: cd-image 25 | 26 | lint: 27 | @cpplint --quiet --recursive --linelength=160 --filter=-readability/casting,-build/include_order --root=Source Source 28 | 29 | # We add bochs-display so we can get a QEMU framebuffer, and an ATI card so we can start writing a driver for that. 30 | qemu-x86_32: image-x86_32 cd-image 31 | @qemu-system-i386 \ 32 | -no-reboot -no-shutdown \ 33 | --machine pc -cpu pentium -m 16 \ 34 | -cdrom Amethyst.iso \ 35 | -device ati-vga,model=rage128pro \ 36 | -serial stdio \ 37 | -device pvpanic 38 | 39 | # -device bochs-display \ 40 | 41 | cd-image: Build/kernel32 resources 42 | -@grub-mkrescue -o Amethyst.iso Build -quiet 43 | 44 | disk-image: Build/kernel32 resources 45 | dd if=/dev/zero of=disk.img bs=512 count=131072 46 | sudo losetup /dev/loop1 disk.img 47 | sudo sfdisk --force /dev/loop1 -u S < Resources/diskformat 48 | sudo sync 49 | sudo losetup -d /dev/loop1 50 | sudo sync 51 | 52 | # This line is prone to breakage! We assume it goes on loop0, but it could go on any loop device. We need to save it to a variable or something. 53 | sudo losetup -P -f disk.img --show 54 | sudo mkdosfs -F32 -f 2 /dev/loop0p1 55 | sudo mount /dev/loop0p1 /mnt 56 | sudo grub-install --boot-directory=/mnt/boot --no-floppy /dev/loop0 57 | sudo cp -r Build/* /mnt 58 | sudo sync 59 | sudo umount /mnt 60 | sudo losetup -d /dev/loop0 61 | 62 | disk-image-cleanup: 63 | sudo umount /mnt 64 | sudo losetup -d /dev/loop0 65 | sudo losetup -d /dev/loop1 66 | 67 | resources: 68 | -@mkdir -p Build/boot/grub 69 | @cp Resources/grub.cfg Build/boot/grub 70 | @cp Resources/Fonts Build -r 71 | @cp Resources/Images Build -r 72 | 73 | #Custom file build targets: 74 | Build/kernel32: Source/arch/x86_32/entry.o Source/kernel.o Source/shell.o Source/MouseMoveEvent.o Source/arch/x86_32/physicalMemory.o \ 75 | Source/graphics.o Source/StandardIO.o Source/arch/x86_32/taskswitch.o Source/arch/x86_32/thread.o Source/library/memory.o Source/cpuid.o Source/debug.o Source/library/fifobuffer.o \ 76 | Source/arch/x86_32/rootDevice.o Source/stream.o Source/deviceTree.o Source/arch/x86_32/portIO.o Source/arch/x86_32/gdt.o \ 77 | Source/arch/x86_32/interrupts_setup.o Source/arch/x86_32/interrupts_handler.o Source/arch/x86_32/interrupts_stubs.o Source/Clock.o Source/memoryManager.o Source/library/string.o \ 78 | Source/drivers/pciBus.o Source/drivers/pci/deviceNames.o Source/drivers/ps2controller.o Source/drivers/keyboard.o Source/drivers/mouse.o Source/drivers/serial.o Source/drivers/piixide.o Source/drivers/qemu_display_driver.o Source/drivers/atiRage128.o \ 79 | Source/GUI/Widget.o Source/GUI/Desktop.o Source/GUI/Window.o Source/GUI/TextConsole.o Source/GUI/TextLabel.o Source/GUI/TargaImage.o 80 | -@mkdir -p Build 81 | @$(LD) -T Resources/Linker-Script.ld -ffreestanding -nostdlib -lgcc -o $@ $^ 82 | 83 | # Object-file compilation rules: 84 | %.o: %.c 85 | @$(CC) $(CFLAGS) -c $< -o $@ 86 | 87 | %.o: %.cpp 88 | @$(CC) $(CPPFLAGS) -c $< -o $@ 89 | 90 | %.o: %.S 91 | @$(AS) $(CFLAGS) -c $< -o $@ 92 | 93 | %.o: %.asm 94 | @nasm -felf32 $< -o $@ 95 | -------------------------------------------------------------------------------- /Source/arch/x86_32/physicalMemory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * The virtual (so we can access it) address of the physical memory allocation bitmap. 6 | */ 7 | #define PHYSICALMEMORY_BITMAPADDRESS (void*)0xC0060000 8 | 9 | /** 10 | * The size of the physical memory allocation bitmap, in bytes. 11 | */ 12 | #define PHYSICALMEMORY_BITMAPSIZE 0x20000 13 | 14 | uint32 physicalMemory_findEndOfReservedMemory(struct multiboot_moduleNode* module, uint32 count) { 15 | uint32 endOfReservedMemory = (uint32) &_kernel_end; 16 | 17 | for (uint32 i = 0; i < count; i++) { 18 | if (module[i].end > (void*) endOfReservedMemory) { 19 | endOfReservedMemory = (uint32) module[i].end; 20 | } 21 | } 22 | 23 | return endOfReservedMemory; 24 | } 25 | 26 | /** 27 | * Call this initialisation AFTER paging and GDT, but before any other memory stuff (virtual or heap). 28 | */ 29 | 30 | void physicalMemory_initialise(struct multiboot_info* multibootData) { 31 | // the easiest way to do this is to mark the whole area as allocated and then free the bits that are ram. 32 | 33 | // Mark 128KiB of RAM at physical 0x60000 with zeroes (representing allocated memory). 34 | memzero(PHYSICALMEMORY_BITMAPADDRESS, PHYSICALMEMORY_BITMAPSIZE); 35 | 36 | // Mark all available RAM as free. 37 | for(uint32 i = 0; i < (multibootData->memoryMapLength / 20) - 1; i++) { 38 | struct multiboot_memoryMapNode mem = (multibootData->memoryMapAddress[i]); 39 | 40 | // uint64 end = mem.addr + mem.len - 1; 41 | 42 | if(mem.type == 1) { 43 | // mark free. 44 | 45 | uint32 pages = mem.len >> 12; 46 | 47 | // TODO(JackScottAU): Make this something other than the least efficient way possible of doing this. 48 | for(uint32 j = 0; j < pages; j++) { 49 | physicalMemory_free((void*)mem.addr); 50 | } 51 | } 52 | } 53 | 54 | // Mark all RAM below end of kernel/modules as allocated. 55 | uint32 end = physicalMemory_findEndOfReservedMemory(multibootData->modsAddr, multibootData->modsCount); 56 | 57 | uint32 mpages = end >> 12; 58 | 59 | // TODO(JackScottAU): Make this something other than the least efficient way possible of doing this. 60 | for(uint32 j = 0; j < mpages; j++) { 61 | physicalMemory_markAllocated((void*)(j << 12)); 62 | } 63 | } 64 | 65 | void* physicalMemory_allocate() { 66 | uint8* bitmap = (uint8*)PHYSICALMEMORY_BITMAPADDRESS; 67 | 68 | // TODO: hold a persistent index to the first byte with free memory to speed up this search. 69 | 70 | for(uint32 i = 0; i < PHYSICALMEMORY_BITMAPSIZE; i++) { // iterate through 128K of memory. 71 | // debug(LOGLEVEL_TRACE, "Index %d: %h", i, bitmap[i]); 72 | 73 | if(bitmap[i] != 0x00) { 74 | 75 | for(uint8 b = 0; b < 8; b++) { 76 | if((bitmap[i] >> b) & 0x01) { 77 | // the b-th bit of this index is free, we can return that. 78 | uint8 mask = 0x01 << b; 79 | bitmap[i] = bitmap[i] & !mask; 80 | 81 | return (void*)(((i * 8) + b) << 12); 82 | } 83 | } 84 | 85 | // return (i * 8) << 12; 86 | } 87 | } 88 | 89 | return (void*)0xFFFFFFFF; // no memory found. 90 | } 91 | 92 | // not sure this is actually needed? 93 | void physicalMemory_markAllocated(void* address) { 94 | uint8* bitmap = (uint8*)PHYSICALMEMORY_BITMAPADDRESS; 95 | 96 | uint32 index = ((uint32)address >> 12) / 8; 97 | uint32 bit = ((uint32)address >> 12) % 8; 98 | 99 | uint8 mask = 0x01 << bit; 100 | 101 | bitmap[index] = bitmap[index] & !mask; 102 | } 103 | 104 | void physicalMemory_free(void* address) { 105 | uint8* bitmap = (uint8*)PHYSICALMEMORY_BITMAPADDRESS; 106 | 107 | uint32 index = ((uint32)address >> 12) / 8; 108 | uint32 bit = ((uint32)address >> 12) % 8; 109 | 110 | uint8 mask = 0x01 << bit; 111 | 112 | bitmap[index] = bitmap[index] | mask; 113 | } 114 | -------------------------------------------------------------------------------- /Source/deviceTree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Device and driver tree core functionality. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void deviceTree_printInternal(void (*putChar)(char), deviceTree_Entry* device, uint8 depth, bool detailedInfo); 12 | 13 | deviceTree_Entry* deviceTree_createDevice(char* name, uint32 type, void* data) { 14 | deviceTree_Entry* device = memoryManager_allocate(sizeof(deviceTree_Entry)); 15 | 16 | device->next = NULL; 17 | device->child = NULL; 18 | device->name = name; 19 | device->type = type; 20 | device->data = data; 21 | 22 | return device; 23 | } 24 | 25 | void deviceTree_addSibling(deviceTree_Entry* attached, deviceTree_Entry* toAttach) { 26 | while (attached->next != NULL) { 27 | attached = attached->next; 28 | } 29 | 30 | attached->next = toAttach; 31 | } 32 | 33 | void deviceTree_addChild(deviceTree_Entry* parent, deviceTree_Entry* toAttach) { 34 | deviceTree_Entry* child = parent->child; 35 | 36 | if (child != NULL) { 37 | deviceTree_addSibling(child, toAttach); 38 | } else { 39 | // Add first child. 40 | parent->child = toAttach; 41 | } 42 | } 43 | 44 | void deviceTree_print(void (*putChar)(char), bool detailedInfo) { 45 | deviceTree_printInternal(putChar, deviceTree_get(), 0, detailedInfo); 46 | } 47 | 48 | void deviceTree_printInternal(void (*putChar)(char), deviceTree_Entry* device, uint8 depth, bool detailedInfo) { 49 | if (device == NULL) { 50 | return; 51 | } 52 | 53 | if (depth > 0) { 54 | for (int i = 0; i < depth - 1; i++) { 55 | stream_printf(putChar, " | "); 56 | } 57 | 58 | stream_printf(putChar, " +- "); 59 | } 60 | 61 | // TODO(JackScottAU): print this in bold. 62 | stream_printf(putChar, "\033[32m"); 63 | stream_printf(putChar, "%s\n", device->name); 64 | stream_printf(putChar, "\033[0m"); 65 | 66 | // TODO(JackScottAU): print more info if detailedInfo is set (need to put resources and PCI class etc in here). 67 | if (detailedInfo) { 68 | // todo 69 | 70 | // if(type = pci) { pci_printMoreInfo(entry, depth) } to show class/subclass/if/revision and Bus/Slot/Function 71 | // to make this work need to put bus/slot/function into devicetree entry. 72 | 73 | if (device->type == DEVICETREE_TYPE_PCI) { 74 | pciBus_printDeviceInformation(putChar, device->data, depth); 75 | } 76 | 77 | // then interate through attached i/o, mem, irq resources, etc 78 | for (uint32 i = 0; i < device->ResourceCount; i++) { 79 | for (uint8 j = 0; j < depth; j++) { 80 | stream_printf(putChar, " | "); 81 | } 82 | 83 | DeviceResource resource = device->Resources[i]; 84 | 85 | uint32 end = resource.StartAddress + resource.Length - 1; 86 | 87 | switch (resource.Type) { 88 | case DEVICE_RESOURCETYPE_MEM: 89 | stream_printf(putChar, "Resource (MEM): %h -> %h (%h)\n", resource.StartAddress, end, resource.Length); 90 | break; 91 | 92 | case DEVICE_RESOURCETYPE_IO: 93 | stream_printf(putChar, "Resource (I/O): %h -> %h (%h)\n", resource.StartAddress, end, resource.Length); 94 | break; 95 | 96 | case DEVICE_RESOURCETYPE_IRQ: 97 | stream_printf(putChar, "Resource (IRQ): %h\n", resource.Flags); 98 | break; 99 | 100 | default: 101 | stream_printf(putChar, "Resource (Unknown)\n"); 102 | break; 103 | } 104 | } 105 | } 106 | 107 | // Print children, then come back here and print the next sibling to us. 108 | deviceTree_printInternal(putChar, device->child, depth + 1, detailedInfo); 109 | deviceTree_printInternal(putChar, device->next, depth, detailedInfo); 110 | } 111 | -------------------------------------------------------------------------------- /Source/stream.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Stream input/output functions. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void stream_putDecimal(void (*putChar)(char), sint32 arg); 13 | void stream_putHexadecimal(void (*putChar)(char), uint32 arg, uint8 leadingZeroes); 14 | void stream_putHexadecimalInternal(void (*putChar)(char), uint32 arg); 15 | 16 | char* stream_readLine(char (*getChar)(void), void (*putChar)(char), bool echoMode) { 17 | // hopelessly naive implementation that ignores memory safety entirely! 18 | 19 | char* data = memoryManager_allocate(100); 20 | 21 | int i = 0; 22 | 23 | data[i] = getChar(); 24 | 25 | if (echoMode) { 26 | putChar(data[i]); 27 | } 28 | 29 | while (data[i] != '\n') { 30 | if (data[i] != '\b') { 31 | i++; 32 | } else { 33 | // backspace handling. 34 | i--; 35 | if (i < 0) { 36 | break; 37 | } 38 | } 39 | 40 | data[i] = getChar(); 41 | 42 | if (echoMode) { 43 | putChar(data[i]); 44 | } 45 | } 46 | 47 | data[i] = 0; // end string. 48 | return data; 49 | } 50 | 51 | void stream_printf(void (*putChar)(char), const char* formatString, ...) { 52 | va_list args; 53 | va_start(args, formatString); 54 | 55 | stream_vprintf(putChar, formatString, args); 56 | 57 | va_end(args); 58 | } 59 | 60 | void stream_vprintf(void (*putChar)(char), const char* formatString, va_list args) { 61 | int i = 0; 62 | int arg; 63 | char* sarg; 64 | 65 | while (formatString[i]) { 66 | if (formatString[i] == '\n') { 67 | // output a carriage return as well. 68 | putChar('\r'); // the actual \n is done down below. 69 | } 70 | 71 | if (formatString[i] == '%') { 72 | i++; 73 | 74 | if (formatString[i] == '%') { 75 | putChar('%'); 76 | } 77 | 78 | if (formatString[i] == 'd') { 79 | arg = va_arg(args, int); 80 | stream_putDecimal(putChar, arg); 81 | } 82 | 83 | if (formatString[i] == 'h') { 84 | arg = va_arg(args, int); 85 | stream_putHexadecimal(putChar, arg, 0); 86 | } 87 | 88 | if (formatString[i] == 'H') { 89 | arg = va_arg(args, int); 90 | stream_putHexadecimal(putChar, arg, 1); 91 | } 92 | 93 | if (formatString[i] == 's') { 94 | sarg = va_arg(args, char*); 95 | // Write a string. 96 | while (*sarg) { 97 | putChar(*sarg); 98 | sarg++; 99 | } 100 | } 101 | 102 | } else { 103 | putChar(formatString[i]);; 104 | } 105 | 106 | i++; 107 | } 108 | } 109 | 110 | void stream_putDecimal(void (*putChar)(char), sint32 arg) { 111 | if (arg < 0) { 112 | putChar('-'); 113 | arg = arg * -1; 114 | } 115 | 116 | if (arg/10 >= 1) { 117 | stream_putDecimal(putChar, arg/10); 118 | } 119 | 120 | putChar((arg%10)+'0'); 121 | } 122 | 123 | void stream_putHexadecimalInternal(void (*putChar)(char), uint32 arg) { 124 | if (arg/16 >= 1) { 125 | stream_putHexadecimalInternal(putChar, arg/16); 126 | } 127 | 128 | if ((arg%16) < 10) { 129 | putChar('0'+(arg%16)); 130 | } else { 131 | putChar('A'+((arg%16)-10)); 132 | } 133 | } 134 | 135 | void stream_putHexadecimal(void (*putChar)(char), uint32 arg, uint8 leadingZeroes) { 136 | stream_printf(putChar, "0x"); 137 | 138 | if (leadingZeroes) { 139 | int j; 140 | 141 | for (int i=28; i >= 0; i -= 4) { 142 | j = (arg & (0xF << i)) >> i; 143 | 144 | if (j < 10) { 145 | putChar('0'+j); 146 | } else { 147 | putChar('A'+(j-10)); 148 | } 149 | } 150 | } else { 151 | stream_putHexadecimalInternal(putChar, arg); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Source/drivers/qemu_display_driver.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Driver for QEMU Standard Display Adapter. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | * 6 | * This driver is also used by Bochs (where it originated). 7 | * 8 | * 2D Capabilities: 9 | * [X] 2D Framebuffer 10 | * [-] Double Buffering 11 | * [ ] Hardware Sprites (Cursor) 12 | * [ ] Hardware Copy and Fill (Blitting) 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | uint32 qemuVga_width = 0; 21 | uint32 qemuVga_height = 0; 22 | 23 | deviceTree_Entry* qemu_device = NULL; 24 | 25 | /** 26 | * ID register that tells us the version of the device. 27 | */ 28 | #define QEMUVGA_REGISTER_ID 0x0000 29 | #define QEMUVGA_REGISTER_WIDTH 0x0001 30 | #define QEMUVGA_REGISTER_HEIGHT 0x0002 31 | #define QEMUVGA_REGISTER_DEPTH 0x0003 32 | #define QEMUVGA_REGISTER_STATUS 0x0004 33 | 34 | #define QEMUVGA_STATUS_DISABLED 0x0000 35 | #define QEMUVGA_STATUS_ENABLED 0x0001 36 | #define QEMUVGA_STATUS_LINEAR 0x0040 37 | 38 | deviceTree_Entry* qemuVga_initialise(pciBus_Entry* pciDetails) { 39 | uint32 bus = pciDetails->bus; 40 | uint32 slot = pciDetails->slot; 41 | uint32 function = pciDetails->function; 42 | PageDirectory* pg = memoryManager_getCurrentPageDirectory(); 43 | 44 | uint32 bar0 = pci_getBar(bus, slot, function, 0) & 0xFFFFFFF0; 45 | uint32 bar0size = pci_getBarSize(bus, slot, function, 0); 46 | // TODO(JackScottAU): check the size of the memory BAR and allocate more pages if needed. 47 | uint32 bar2 = pci_getBar(bus, slot, function, 2) & 0xFFFFFFFC; 48 | memoryManager_mapPhysicalMemoryPage(pg, (void*)0xFFC00000, (void*)bar0, 1024); 49 | memoryManager_mapPhysicalMemoryPage(pg, (void*)0xFFBFF000, (void*)bar2, 1); 50 | debug(LOGLEVEL_INFO, "VESA BAR0: %h", bar0); 51 | debug(LOGLEVEL_INFO, "VESA BAR2: %h", bar2); 52 | 53 | // BAR1: Reserved for 64-bit framebuffer addresses. 54 | // ROM: Not needed. 55 | // IRQ: None. 56 | 57 | // memoryManager_printMemoryMap(pg); 58 | 59 | // can now read registers. 60 | uint16* bochsRegs = (uint16*) 0xFFBFF500; 61 | 62 | if (bochsRegs[QEMUVGA_REGISTER_ID] < 0xB0C2) { 63 | // Display adapter isn't good enough. 64 | // TODO(JackScottAU): unmap physdical memory pages we don't need. 65 | return NULL; 66 | } 67 | 68 | deviceTree_Entry* device = deviceTree_createDevice("QEMU Standard Display Adapter", DEVICETREE_TYPE_PCI, pciDetails); 69 | 70 | device->Resources = memoryManager_allocate(sizeof(DeviceResource) * 2); 71 | device->ResourceCount = 2; 72 | 73 | device->Resources[0].Type = DEVICE_RESOURCETYPE_MEM; 74 | device->Resources[0].StartAddress = bar0; 75 | device->Resources[0].Length = pci_getBarSize(bus, slot, function, 0); 76 | device->Resources[1].Type = DEVICE_RESOURCETYPE_MEM; 77 | device->Resources[1].StartAddress = bar2; 78 | device->Resources[1].Length = 0x1000; // 4KiB 79 | 80 | deviceTree_Entry* monitor = deviceTree_createDevice("Generic Monitor", DEVICETREE_TYPE_OTHER, 0); 81 | 82 | deviceTree_addChild(device, monitor); 83 | 84 | qemu_device = device; 85 | 86 | return device; 87 | } 88 | 89 | Canvas* qemuVga_getCanvas() { 90 | // Check to make sure a mode has been set. 91 | if (qemuVga_width == 0 || qemuVga_height == 0) { 92 | debug(LOGLEVEL_ERROR, "No video mode has been set, cannot create a canvas."); 93 | return NULL; 94 | } 95 | 96 | Canvas* canvas = (Canvas*)memoryManager_allocate(sizeof(Canvas)); 97 | canvas->framebuffer = (void*)0xFFC00000; 98 | canvas->height = qemuVga_height; 99 | canvas->width = qemuVga_width; 100 | 101 | return canvas; 102 | } 103 | 104 | void qemuVga_setMode(uint16 width, uint16 height) { 105 | uint16* bochsRegs = (uint16*) 0xFFBFF500; 106 | 107 | qemuVga_width = width; 108 | qemuVga_height = height; 109 | 110 | // BOCHS DISPLAY SETUP. 111 | bochsRegs[QEMUVGA_REGISTER_STATUS] = QEMUVGA_STATUS_DISABLED; // disable. 112 | bochsRegs[QEMUVGA_REGISTER_WIDTH] = width; 113 | bochsRegs[QEMUVGA_REGISTER_HEIGHT] = height; 114 | bochsRegs[QEMUVGA_REGISTER_DEPTH] = 0x20; 115 | bochsRegs[QEMUVGA_REGISTER_STATUS] = QEMUVGA_STATUS_ENABLED | QEMUVGA_STATUS_LINEAR; // enable, plus enable LFB. 116 | } 117 | -------------------------------------------------------------------------------- /Source/arch/x86_32/entry.S: -------------------------------------------------------------------------------- 1 | # Declare constants for the multiboot header. 2 | .set ALIGN, 1<<0 # align loaded modules on page boundaries 3 | .set MEMINFO, 1<<1 # provide memory map 4 | .set VIDINFO, 1<<2 5 | .set FLAGS, ALIGN | MEMINFO #| VIDINFO # this is the Multiboot 'flag' field 6 | .set MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header 7 | .set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot 8 | 9 | .global boot_page_directory 10 | 11 | # Declare a multiboot header that marks the program as a kernel. 12 | .section .multiboot.data, "aw" 13 | .align 4 14 | .long MAGIC 15 | .long FLAGS 16 | .long CHECKSUM 17 | 18 | .long 0 19 | .long 0 20 | .long 0 21 | .long 0 22 | .long 0 23 | 24 | .long 0 25 | .long 1024 26 | .long 768 27 | .long 32 28 | 29 | # Allocate the initial stack. 30 | .section .bootstrap_stack, "aw", @nobits 31 | stack_bottom: 32 | .skip 16384 # 16 KiB 33 | stack_top: 34 | 35 | # Preallocate pages used for paging. Don't hard-code addresses and assume they 36 | # are available, as the bootloader might have loaded its multiboot structures or 37 | # modules there. This lets the bootloader know it must avoid the addresses. 38 | .section .bss, "aw", @nobits 39 | .align 4096 40 | boot_page_directory: 41 | .skip 4096 42 | boot_page_table1: 43 | .skip 4096 44 | # Further page tables may be required if the kernel grows beyond 3 MiB. 45 | 46 | # The kernel entry point. 47 | .section .multiboot.text, "a" 48 | .global _start 49 | .type _start, @function 50 | _start: 51 | 52 | 53 | 54 | 55 | # Physical address of boot_page_table1. 56 | # TODO: I recall seeing some assembly that used a macro to do the 57 | # conversions to and from physical. Maybe this should be done in this 58 | # code as well? 59 | movl $(boot_page_table1 - 0xC0000000), %edi 60 | # First address to map is address 0. 61 | # TODO: Start at the first kernel page instead. Alternatively map the first 62 | # 1 MiB as it can be generally useful, and there's no need to 63 | # specially map the VGA buffer. 64 | movl $0, %esi 65 | # Map 1024 pages. 66 | movl $1024, %ecx 67 | 68 | 1: 69 | # cmpl $(_kernel_end - 0xC0000000), %esi 70 | # jge 3f 71 | 72 | # Map physical address as "present, writable". Note that this maps 73 | # .text and .rodata as writable. Mind security and map them as non-writable. 74 | movl %esi, %edx 75 | orl $0x003, %edx 76 | movl %edx, (%edi) 77 | 78 | 2: 79 | # Size of page is 4096 bytes. 80 | addl $4096, %esi 81 | # Size of entries in boot_page_table1 is 4 bytes. 82 | addl $4, %edi 83 | # Loop to the next entry if we haven't finished. 84 | loop 1b 85 | 86 | 3: 87 | # The page table is used at both page directory entry 0 (virtually from 0x0 88 | # to 0x3FFFFF) (thus identity mapping the kernel) and page directory entry 89 | # 768 (virtually from 0xC0000000 to 0xC03FFFFF) (thus mapping it in the 90 | # higher half). The kernel is identity mapped because enabling paging does 91 | # not change the next instruction, which continues to be physical. The CPU 92 | # would instead page fault if there was no identity mapping. 93 | 94 | # Map the page table to both virtual addresses 0x00000000 and 0xC0000000. 95 | movl $(boot_page_table1 - 0xC0000000 + 0x003), boot_page_directory - 0xC0000000 + 0 96 | movl $(boot_page_table1 - 0xC0000000 + 0x003), boot_page_directory - 0xC0000000 + 768 * 4 97 | 98 | # Set cr3 to the address of the boot_page_directory. 99 | movl $(boot_page_directory - 0xC0000000), %ecx 100 | movl %ecx, %cr3 101 | 102 | # Enable paging and the write-protect bit. 103 | movl %cr0, %ecx 104 | orl $0x80010000, %ecx 105 | movl %ecx, %cr0 106 | 107 | # Jump to higher half with an absolute jump. 108 | lea 4f, %ecx 109 | jmp *%ecx 110 | 111 | .section .text 112 | 113 | 4: 114 | # At this point, paging is fully set up and enabled. 115 | 116 | # Unmap the identity mapping as it is now unnecessary. 117 | movl $0, boot_page_directory + 0 118 | 119 | # Reload crc3 to force a TLB flush so the changes to take effect. 120 | movl %cr3, %ecx 121 | movl %ecx, %cr3 122 | 123 | # Set up the stack. 124 | mov $stack_top, %esp 125 | 126 | # We can now push the multiboot header arguments used by the kernel. 127 | push %ebx #Push the Multiboot data structure. 128 | push %eax #Push the Multiboot magic number. 129 | 130 | # Enter the high-level kernel. 131 | call kernel_initialise 132 | 133 | # Infinite loop if the system has nothing more to do. 134 | cli 135 | 1: hlt 136 | jmp 1b 137 | -------------------------------------------------------------------------------- /Source/GUI/TargaImage.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // Code ripped wholesale from https://wiki.osdev.org/Loading_Icons 8 | TargaImage::TargaImage(uint8* ptr, uint32 length, uint32 locx, uint32 locy, Canvas* canvas) 9 | { 10 | this->children = new LinkedList(); 11 | this->canvas = canvas; 12 | this->x = locx; 13 | this->y = locy; 14 | 15 | uint32* data; 16 | uint32 i, j, k, x, y; 17 | uint32 o = (ptr[11] << 8) + ptr[10]; 18 | uint32 m = ((ptr[1]? (ptr[7]>>3)*ptr[5] : 0) + 18); 19 | 20 | this->w = (ptr[13] << 8) + ptr[12]; 21 | this->h = (ptr[15] << 8) + ptr[14]; 22 | 23 | // if(w<1 || h<1) return NULL; 24 | 25 | data = (uint32*)memoryManager_allocate((w*h+2)*sizeof(unsigned int)); 26 | // if(!data) return NULL; 27 | 28 | switch(ptr[2]) { 29 | case 1: 30 | if(ptr[6]!=0 || ptr[4]!=0 || ptr[3]!=0 || (ptr[7]!=24 && ptr[7]!=32)) { memoryManager_free(data); } 31 | for(y=i=0; y>3) + 18; 35 | data[2 + i++] = ((ptr[7]==32?ptr[j+3]:0xFF) << 24) | (ptr[j+2] << 16) | (ptr[j+1] << 8) | ptr[j]; 36 | } 37 | } 38 | break; 39 | case 2: 40 | if(ptr[5]!=0 || ptr[6]!=0 || ptr[1]!=0 || (ptr[16]!=24 && ptr[16]!=32)) { memoryManager_free(data); } 41 | for(y=i=0; y>3)); 43 | for(x=0; x>3; 46 | } 47 | } 48 | break; 49 | case 9: 50 | if(ptr[6]!=0 || ptr[4]!=0 || ptr[3]!=0 || (ptr[7]!=24 && ptr[7]!=32)) { memoryManager_free(data); } 51 | y = i = 0; 52 | for(x=0; x 127) { 55 | k -= 127; x += k; 56 | j = ptr[m++]*(ptr[7]>>3) + 18; 57 | while(k--) { 58 | if(!(i%w)) { i=((!o?h-y-1:y)*w); y++; } 59 | data[2 + i++] = ((ptr[7]==32?ptr[j+3]:0xFF) << 24) | (ptr[j+2] << 16) | (ptr[j+1] << 8) | ptr[j]; 60 | } 61 | } else { 62 | k++; x += k; 63 | while(k--) { 64 | j = ptr[m++]*(ptr[7]>>3) + 18; 65 | if(!(i%w)) { i=((!o?h-y-1:y)*w); y++; } 66 | data[2 + i++] = ((ptr[7]==32?ptr[j+3]:0xFF) << 24) | (ptr[j+2] << 16) | (ptr[j+1] << 8) | ptr[j]; 67 | } 68 | } 69 | } 70 | break; 71 | case 10: 72 | if(ptr[5]!=0 || ptr[6]!=0 || ptr[1]!=0 || (ptr[16]!=24 && ptr[16]!=32)) { memoryManager_free(data); } 73 | y = i = 0; 74 | for(x=0; x 127) { 77 | k -= 127; x += k; 78 | while(k--) { 79 | if(!(i%w)) { i=((!o?h-y-1:y)*w); y++; } 80 | data[2 + i++] = ((ptr[16]==32?ptr[m+3]:0xFF) << 24) | (ptr[m+2] << 16) | (ptr[m+1] << 8) | ptr[m]; 81 | } 82 | m += ptr[16]>>3; 83 | } else { 84 | k++; x += k; 85 | while(k--) { 86 | if(!(i%w)) { i=((!o?h-y-1:y)*w); y++; } 87 | data[2 + i++] = ((ptr[16]==32?ptr[m+3]:0xFF) << 24) | (ptr[m+2] << 16) | (ptr[m+1] << 8) | ptr[m]; 88 | m += ptr[16]>>3; 89 | } 90 | } 91 | } 92 | break; 93 | default: 94 | memoryManager_free(data); 95 | } 96 | 97 | this->pixels = &data[2]; 98 | 99 | data[0] = w; 100 | data[1] = h; 101 | 102 | Redraw(); 103 | } 104 | 105 | void TargaImage::Redraw() { 106 | // draw. 107 | for(uint32 i = 0; i < h; i++) { 108 | for(uint32 j = 0; j < w; j++) { 109 | uint32 pixel = pixels[(j * h + i)]; 110 | 111 | canvas_putPixel(canvas, x + j, y + i, pixel); 112 | } 113 | } 114 | } 115 | 116 | void TargaImage::HandleUIEvent(GuiEvent* eventData) { 117 | 118 | } 119 | 120 | -------------------------------------------------------------------------------- /Source/arch/x86_32/interrupts_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Interrupts handler functionality. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | interrupts_handlerCallback* interrupts_callbacks = (interrupts_handlerCallback*) NULL; 17 | 18 | /** 19 | * @param Registers The state of the registers on the stack provided by the ISR stub. 20 | */ 21 | void interrupts_handler(struct Registers_S *Registers) { 22 | // If it is an exception. 23 | if (Registers->IntNum <= 0x1F) { 24 | debug(LOGLEVEL_CRITICAL, "\033[37m\033[41m\n"); 25 | debug(LOGLEVEL_CRITICAL, " "); 26 | debug(LOGLEVEL_CRITICAL, " ! SYSTEM PANIC ! "); 27 | debug(LOGLEVEL_CRITICAL, " "); 28 | debug(LOGLEVEL_CRITICAL, "\033[0m\n"); 29 | 30 | debug(LOGLEVEL_CRITICAL, "Interrupt number: %d\tError code: %h\n", Registers->IntNum, Registers->ErrorCode); 31 | debug(LOGLEVEL_CRITICAL, "EAX: %h\tEBX: %h\tECX: %h\tEDX: %h\n", Registers->EAX, Registers->EBX, Registers->ECX, Registers->EDX); 32 | debug(LOGLEVEL_CRITICAL, "CS: %h\tDS: %h\tES: %h\tFS: %h\tGS: %h\t\n", Registers->CS, Registers->DS, Registers->ES, Registers->FS, Registers->GS); 33 | debug(LOGLEVEL_CRITICAL, "EDI: %h\tESI: %h\tEBP: %h\tESP: %h\n", Registers->EDI, Registers->ESI, Registers->EBP, Registers->ESP); 34 | debug(LOGLEVEL_CRITICAL, "EIP: %h\tEFlags: %h\tUserESP: %h\tSS: %h\n", Registers->EIP, Registers->EFlags, Registers->UserESP, Registers->SS); 35 | 36 | interrupts_disableInterrupts(); 37 | haltCPU(); 38 | } 39 | 40 | // Check all interrupt handler callbacks to see if any need to be processed. 41 | interrupts_handlerCallback* current = interrupts_callbacks; 42 | while (current) { 43 | // Check to see if this callback is for the current interrupt number. 44 | if ((current->interruptNumber == Registers->IntNum)) { 45 | // We need to process this interrupt callback by calling the stored function. 46 | void (*foo)(uint32); // Declare variable to store the function pointer. 47 | foo = current->funcToCall; // Retrieve function pointer. 48 | (*foo)(current->arbitraryNumber); // Call the function. 49 | } 50 | current = current->next; 51 | } 52 | 53 | // If it is an IRQ. 54 | if ((Registers->IntNum >= 0x20) && (Registers->IntNum <= 0x2F)) { 55 | // Send EOI to 8259 controller. 56 | if (Registers->IntNum >= 0x28) { 57 | // If it is the slave, send it to that. 58 | portIO_write8(0xA0, 0x20); 59 | } 60 | 61 | // But send it to the master no matter what. 62 | portIO_write8(0x20, 0x20); 63 | } 64 | 65 | scheduler(); 66 | } 67 | 68 | interrupts_handlerCallback* interrupts_addHandler(uint8 interruptNumber, uint32 argument, void (* callback)(uint32)) { 69 | interrupts_handlerCallback* request = memoryManager_allocate(sizeof(interrupts_handlerCallback)); 70 | 71 | request->interruptNumber = interruptNumber; 72 | request->arbitraryNumber = argument; 73 | request->funcToCall = callback; 74 | 75 | // Add it to the front of the list. We'll figure out sorting it later. 76 | request->next = interrupts_callbacks; 77 | interrupts_callbacks = request; 78 | 79 | return(request); 80 | } 81 | 82 | void interrupts_removeHandler(interrupts_handlerCallback* request) { 83 | interrupts_handlerCallback* current = interrupts_callbacks; 84 | interrupts_handlerCallback* oldCurrent = 0; 85 | 86 | if (current == request) { 87 | interrupts_callbacks = current->next; 88 | memoryManager_free(current); 89 | } 90 | 91 | while (current) { 92 | // Perform check. 93 | if (current == request) { 94 | // Remove it from the list. 95 | interrupts_handlerCallback* temp = current; 96 | 97 | oldCurrent->next = current->next; 98 | current = oldCurrent->next; 99 | 100 | memoryManager_free(temp); 101 | } else { 102 | oldCurrent = current; 103 | current = current->next; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Source/drivers/serial.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Serial driver. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define SERIAL_CLOCKSPEED 115200 14 | 15 | void serial_registerHandler(void); 16 | int serial_testLoopBack(uint16 baseAddress); 17 | 18 | /** 19 | * 0 if there is a device. any other number is no device. 20 | */ 21 | int serial_detect(uint16 baseAddress) { 22 | portIO_write8(baseAddress + 7, 0xAE); 23 | 24 | uint8 value = portIO_read8(baseAddress + 7); 25 | 26 | if (value != 0xAE) { 27 | return 1; 28 | } 29 | 30 | if (serial_testLoopBack(baseAddress)) { 31 | return 1; 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | int serial_init(uint16 baseAddress, uint8 divisor) { 38 | portIO_write8(baseAddress + 3, 0x80); // Enable DLAB (set baud rate divisor) 39 | portIO_write8(baseAddress + 0, divisor); // Set divisor to 3 (lo byte) 38400 baud 40 | portIO_write8(baseAddress + 1, 0x00); // (hi byte) 41 | portIO_write8(baseAddress + 3, 0x03); // 8 bits, no parity, one stop bit (disabled DLAB) 42 | portIO_write8(baseAddress + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold 43 | portIO_write8(baseAddress + 4, 0x0B); // IRQs enabled, RTS/DSR set 44 | portIO_write8(baseAddress + 4, 0x0F); // Data Terminal Ready, Request To Send, Out 1, Out 2 all enabled. 45 | portIO_write8(baseAddress + 1, 0x01); // Enable data interrupts 46 | 47 | if (serial_testLoopBack(baseAddress)) { 48 | return 1; 49 | } 50 | 51 | serial_registerHandler(); 52 | 53 | return 0; 54 | } 55 | 56 | /** 57 | * Tests the serial device loopback. Returns 0 on success, any other number is failure. Returns state to how it was before test, if test was successful. 58 | */ 59 | int serial_testLoopBack(uint16 baseAddress) { 60 | uint8 modemControlRegisterState = portIO_read8(baseAddress + 4); 61 | uint8 interruptControlRegisterState = portIO_read8(baseAddress + 1); 62 | 63 | portIO_write8(baseAddress + 4, 0x1E); // Set in loopback mode, test the serial chip 64 | portIO_write8(baseAddress + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte) 65 | 66 | // Check if serial is faulty (i.e: not same byte as sent) 67 | if (portIO_read8(baseAddress + 0) != 0xAE) { 68 | return 1; 69 | } 70 | 71 | portIO_write8(baseAddress + 4, modemControlRegisterState); 72 | portIO_write8(baseAddress + 1, interruptControlRegisterState); 73 | 74 | return 0; 75 | } 76 | 77 | FIFOBuffer* serial_readbuffer; 78 | 79 | bool serial_echoMode = true; 80 | 81 | int serial_received(void); 82 | 83 | void serial_interruptHandler(uint32 eventData); 84 | 85 | void serial_interruptHandler(uint32 eventData) { 86 | uint8 data = portIO_read8(SERIAL_COM1); 87 | 88 | FIFOBuffer_WriteBytes(serial_readbuffer, &data, 1); 89 | 90 | if (serial_echoMode) { 91 | serial_writeChar(data); 92 | } 93 | } 94 | 95 | void serial_registerHandler() { 96 | interrupts_addHandler(0x24, 0, (*serial_interruptHandler)); 97 | 98 | serial_readbuffer = FIFOBuffer_new(1024); 99 | } 100 | 101 | /** 102 | * Whether the serial read buffer has data. 0 = no data. otherwise returns size of data waiting to be read in bytes. 103 | */ 104 | int serial_canRead() { 105 | return FIFOBuffer_ContentsSize(serial_readbuffer); 106 | } 107 | 108 | char serial_readChar() { 109 | // If there's nothing in the buffer, say so. 110 | if (serial_canRead() == 0) { 111 | return 0; 112 | } 113 | 114 | uint8 data; 115 | 116 | data = FIFOBuffer_ReadBytes(serial_readbuffer, &data, 1); 117 | 118 | return (char)data; 119 | } 120 | 121 | /** 122 | * Reads the current buffer contents. 123 | */ 124 | char* serial_readString() { 125 | int stringSize = serial_canRead(); 126 | char* string = memoryManager_allocate(sizeof(uint8) * (stringSize + 1)); 127 | 128 | for (int i = 0; i < stringSize; i++) { 129 | string[i] = serial_readChar(); 130 | } 131 | 132 | string[stringSize] = 0; 133 | 134 | return string; 135 | } 136 | 137 | int serial_received() { 138 | return portIO_read8(SERIAL_COM1 + 5) & 1; 139 | } 140 | 141 | char serial_readByte() { 142 | while (serial_received() == 0) { } 143 | 144 | return portIO_read8(SERIAL_COM1); 145 | } 146 | 147 | int is_transmit_empty(void); 148 | int is_transmit_empty() { 149 | return portIO_read8(SERIAL_COM1 + 5) & 0x20; 150 | } 151 | 152 | void serial_writeChar(char a) { 153 | while (is_transmit_empty() == 0) { } 154 | 155 | portIO_write8(SERIAL_COM1, a); 156 | } 157 | 158 | void serial_writeString(const char* string) { 159 | int i = 0; 160 | 161 | while (string[i]) { 162 | serial_writeChar(string[i]); 163 | i++; 164 | } 165 | } 166 | 167 | void serial_writeLine(const char* string) { 168 | serial_writeString(string); 169 | 170 | serial_writeChar('\r'); 171 | serial_writeChar('\n'); 172 | } 173 | -------------------------------------------------------------------------------- /Source/shell.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Shell. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | Shell::Shell(StandardIO* stdio) { 21 | this->stdio = stdio; 22 | this->commands = new LinkedList(); 23 | } 24 | 25 | void Shell::RegisterCommand(const char* commandString, uint32 (* callback)(StandardIO*)) { 26 | ShellCommand* command = new ShellCommand(); 27 | command->callback = callback; 28 | command->command = (char*) commandString; 29 | 30 | commands->Add(command); 31 | } 32 | 33 | void Shell::Main() { 34 | while (1) { 35 | ProcessLine(); 36 | } 37 | } 38 | 39 | void Shell::ProcessLine() { 40 | stdio->Print("> "); 41 | 42 | char* input = stdio->ReadLine(true); 43 | 44 | char** strings = string_split(input, ' '); 45 | 46 | int i = 0; 47 | while (strings[i] != NULL) { 48 | // stdio->Print("string: %s\n", strings[i]); 49 | i++; 50 | } 51 | 52 | char* line = strings[0]; 53 | 54 | debug(LOGLEVEL_DEBUG, "addr of line: %h", line); 55 | 56 | if (*line == 0) { 57 | debug(LOGLEVEL_WARNING, "No input to shell."); 58 | return; 59 | } 60 | 61 | string_toLower(line); 62 | 63 | if (string_compare(line, "get-devicetree") == 0) { 64 | bool verbose = false; 65 | 66 | if (strings[1] != NULL && string_compare(strings[1], "-v") == 0) { 67 | verbose = true; 68 | } 69 | 70 | deviceTree_print(stdio->stdout, verbose); 71 | return; 72 | } 73 | 74 | if (string_compare(line, "get-time") == 0) { 75 | stdio->Print("Time: %h\n", clock_uptime()); 76 | return; 77 | } 78 | 79 | if (string_compare(line, "gfx-demo") == 0) { 80 | // Put something on the screen so we can see if it worked. 81 | uint32* fbmem = (uint32*) 0xFFC00000; 82 | for (int i = 0; i < 1024 * 768; i++) { 83 | fbmem[i] = 0x8888CCCC + i; 84 | } 85 | return; 86 | } 87 | 88 | if (string_compare(line, "get-cpuinformation") == 0) { 89 | CPUID cpuid = CPUID(); 90 | 91 | stdio->Print("Manufacturer: %s\n", cpuid.getManufacturerString()); 92 | stdio->Print("Family: %h\n", cpuid.getFamily()); 93 | stdio->Print("Model: %h\n", cpuid.getModel()); 94 | stdio->Print("Stepping: %h\n", cpuid.getStepping()); 95 | return; 96 | } 97 | 98 | // Triggers a CPU exception for testing the kernel panic screen. 99 | if (string_compare(line, "trigger-exception") == 0) { 100 | uint8* invalidMemoryAddress = (uint8*) 0xA00B8000; 101 | invalidMemoryAddress[0] = 0x00; 102 | } 103 | 104 | if (string_compare(line, "show-gdt") == 0) { 105 | stdio->Print("GDT Pointer:\t%h\n", &gdt_pointer); 106 | stdio->Print("GDT Address:\t%h\n", gdt_table); 107 | 108 | uint16 size = gdt_pointer.size; 109 | 110 | uint8 entries = (gdt_pointer.size + 1) / 8; 111 | 112 | stdio->Print("GDT Size:\t%d (%h bytes)\n\n", entries, size); 113 | 114 | 115 | for (int i = 0; i < entries; i++) { 116 | uint32 base = (gdt_table[i].base_high << 24) + gdt_table[i].base_low; 117 | uint32 limit = (gdt_table[i].limit_high << 16) + gdt_table[i].limit_low; 118 | 119 | if (gdt_table[i].gran) { 120 | // 4K page granularity. 121 | limit = limit << 12 | 0xFFF; 122 | } 123 | 124 | stdio->Print("GDT Entry #%d:\t", i); 125 | 126 | if (limit == 0) { 127 | stdio->Print("NULL Segment\n\n"); 128 | continue; 129 | } 130 | 131 | stdio->Print("Ring-%d ", gdt_table[i].DPL); 132 | if (gdt_table[i].code_data_segment) { 133 | if (gdt_table[i].gran) { 134 | stdio->Print("Page-Aligned "); 135 | } else { 136 | stdio->Print("Byte-Aligned "); 137 | } 138 | 139 | if (gdt_table[i].access & 0x8) { 140 | stdio->Print("Code Segment\n"); 141 | } else { 142 | stdio->Print("Data Segment\n"); 143 | } 144 | } else { 145 | stdio->Print("TSS Segment\n"); 146 | } 147 | 148 | stdio->Print("\t\tBase: %H\t", base); 149 | stdio->Print("Limit: %H\n\n", limit); 150 | } 151 | return; 152 | } 153 | 154 | if (string_compare(line, "shutdown") == 0) { 155 | stdio->Print("Shutting down...\n"); 156 | interrupts_disableInterrupts(); 157 | stdio->Print("It is now safe to turn off your PC."); 158 | haltCPU(); 159 | } 160 | 161 | commands->Reset(); 162 | do { 163 | ShellCommand* command = commands->Current(); 164 | 165 | debug(LOGLEVEL_DEBUG, "Testing shell command: %s", command->command); 166 | 167 | string_toLower(command->command); 168 | 169 | if (string_compare(line, command->command) == 0) { 170 | uint32 result = command->callback(stdio); 171 | 172 | if (result != 0) { 173 | stdio->Print("Command result: %d\n", result); 174 | } 175 | 176 | return; 177 | } 178 | } while (commands->Next()); 179 | 180 | // None of the built-in commands match the input. 181 | stdio->Print("Unknown command.\n"); 182 | } 183 | -------------------------------------------------------------------------------- /Source/arch/x86_32/rootDevice.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - CPUID helper class. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | deviceTree_Entry* deviceTree_rootEntry = NULL; 17 | 18 | deviceTree_Entry* deviceTree_get(void) { 19 | return deviceTree_rootEntry; 20 | } 21 | 22 | void deviceTree_build(void) { 23 | // We should handle what happens if this code gets called twice, but we don't. 24 | 25 | deviceTree_rootEntry = deviceTree_createDevice("x86_32 Root Platform Device", DEVICETREE_TYPE_OTHER, NULL); 26 | 27 | deviceTree_addChild(deviceTree_rootEntry, pciBus_initialise()); 28 | 29 | if (!serial_detect(SERIAL_COM1)) { 30 | deviceTree_Entry* serial = deviceTree_createDevice("ISA Serial Port - COM1", DEVICETREE_TYPE_OTHER, NULL); 31 | 32 | serial->Resources = memoryManager_allocate(sizeof(DeviceResource) * 2); 33 | serial->ResourceCount = 2; 34 | 35 | serial->Resources[0].Type = DEVICE_RESOURCETYPE_IRQ; 36 | serial->Resources[0].Flags = 4; 37 | 38 | serial->Resources[1].Type = DEVICE_RESOURCETYPE_IO; 39 | serial->Resources[1].StartAddress = 0x3F8; 40 | serial->Resources[1].Length = 8; 41 | 42 | deviceTree_addChild(deviceTree_rootEntry, serial); 43 | } 44 | 45 | if (!serial_detect(SERIAL_COM2)) { 46 | deviceTree_Entry* serial = deviceTree_createDevice("ISA Serial Port - COM2", DEVICETREE_TYPE_OTHER, NULL); 47 | 48 | serial->Resources = memoryManager_allocate(sizeof(DeviceResource) * 2); 49 | serial->ResourceCount = 2; 50 | 51 | serial->Resources[0].Type = DEVICE_RESOURCETYPE_IRQ; 52 | serial->Resources[0].Flags = 3; 53 | 54 | serial->Resources[1].Type = DEVICE_RESOURCETYPE_IO; 55 | serial->Resources[1].StartAddress = 0x2F8; 56 | serial->Resources[1].Length = 8; 57 | 58 | deviceTree_addChild(deviceTree_rootEntry, serial); 59 | } 60 | 61 | // Assume a PS/2 controller exists and attempt to initialise it. 62 | deviceTree_Entry* ps2controller = ps2controller_initialise(); 63 | if(ps2controller != NULL) { 64 | deviceTree_addChild(deviceTree_rootEntry, ps2controller); 65 | } 66 | 67 | // See if we have a parallel port. 68 | uint16* parallel1 = (uint16*) 0xC0000408; 69 | debug(LOGLEVEL_DEBUG, "PARALLEL: %h", *parallel1); 70 | uint16* parallel2 = (uint16*) 0xC000040A; 71 | debug(LOGLEVEL_DEBUG, "PARALLEL: %h", *parallel2); 72 | uint16* parallel3 = (uint16*) 0xC000040C; 73 | debug(LOGLEVEL_DEBUG, "PARALLEL: %h", *parallel3); 74 | 75 | if(*parallel1 > 0) { 76 | deviceTree_Entry* parallel = deviceTree_createDevice("Parallel Port - LPT1", DEVICETREE_TYPE_OTHER, NULL); 77 | parallel->Resources = memoryManager_allocate(sizeof(DeviceResource) * 2); 78 | parallel->ResourceCount = 2; 79 | 80 | 81 | parallel->Resources[0].Type = DEVICE_RESOURCETYPE_IRQ; 82 | parallel->Resources[0].Flags = 7; 83 | 84 | parallel->Resources[1].Type = DEVICE_RESOURCETYPE_IO; 85 | parallel->Resources[1].StartAddress = (*parallel1); 86 | parallel->Resources[1].Length = 8; 87 | 88 | deviceTree_addChild(deviceTree_rootEntry, parallel); 89 | } 90 | 91 | if(*parallel2 > 0) { 92 | deviceTree_Entry* parallel = deviceTree_createDevice("Parallel Port - LPT2", DEVICETREE_TYPE_OTHER, NULL); 93 | parallel->Resources = memoryManager_allocate(sizeof(DeviceResource) * 2); 94 | parallel->ResourceCount = 2; 95 | 96 | 97 | parallel->Resources[0].Type = DEVICE_RESOURCETYPE_IRQ; 98 | parallel->Resources[0].Flags = 7; 99 | 100 | parallel->Resources[1].Type = DEVICE_RESOURCETYPE_IO; 101 | parallel->Resources[1].StartAddress = (*parallel2); 102 | parallel->Resources[1].Length = 8; 103 | 104 | deviceTree_addChild(deviceTree_rootEntry, parallel); 105 | } 106 | 107 | if(*parallel3 > 0) { 108 | deviceTree_Entry* parallel = deviceTree_createDevice("Parallel Port - LPT3", DEVICETREE_TYPE_OTHER, NULL); 109 | parallel->Resources = memoryManager_allocate(sizeof(DeviceResource) * 2); 110 | parallel->ResourceCount = 2; 111 | 112 | 113 | parallel->Resources[0].Type = DEVICE_RESOURCETYPE_IRQ; 114 | parallel->Resources[0].Flags = 7; 115 | 116 | parallel->Resources[1].Type = DEVICE_RESOURCETYPE_IO; 117 | parallel->Resources[1].StartAddress = (*parallel3); 118 | parallel->Resources[1].Length = 8; 119 | 120 | deviceTree_addChild(deviceTree_rootEntry, parallel); 121 | } 122 | 123 | /* deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("Real Time Clock?! - have this, need to put in tree", DEVICETREE_TYPE_OTHER, NULL)); 124 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("PIT?!", DEVICETREE_TYPE_OTHER, NULL)); 125 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("8259s? pic", DEVICETREE_TYPE_OTHER, NULL)); 126 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("pc speaker?! - hangs off pit", DEVICETREE_TYPE_OTHER, NULL)); 127 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("parallel prt? - i think this is a 8255 and a few latch chips in disguise", DEVICETREE_TYPE_OTHER, NULL)); 128 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("8257 dma? x2", DEVICETREE_TYPE_OTHER, NULL)); 129 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("fdc controller and empty drive", DEVICETREE_TYPE_OTHER, NULL)); 130 | 131 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("pvpanic / qemu device", DEVICETREE_TYPE_OTHER, NULL)); 132 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("hpet", DEVICETREE_TYPE_OTHER, NULL)); // replacement for pit, along with lapic timer 133 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("ioapic", DEVICETREE_TYPE_OTHER, NULL)); 134 | deviceTree_addChild(deviceTree_rootEntry, deviceTree_createDevice("piix4 i2c", DEVICETREE_TYPE_OTHER, NULL));*/ 135 | } 136 | -------------------------------------------------------------------------------- /Source/Includes/interrupts.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - x86 interrupts support. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #ifndef INCLUDES_INTERRUPTS_H_ 11 | #define INCLUDES_INTERRUPTS_H_ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | //-- Public Data Structures --// 18 | typedef struct interrupts_handlerCallback_s { 19 | void (* funcToCall)(uint32 arbitraryNumber); 20 | uint8 interruptNumber; 21 | uint32 arbitraryNumber; 22 | struct interrupts_handlerCallback_s* next; 23 | } interrupts_handlerCallback; 24 | 25 | //-- Public Interface --// 26 | void interrupts_initialise(void); 27 | interrupts_handlerCallback* interrupts_addHandler(uint8 interruptNumber, uint32 argument, void (* callback)(uint32)); 28 | void interrupts_removeHandler(interrupts_handlerCallback* request); 29 | 30 | //-- Private Interface --// 31 | void interrupts_disableInterrupts(void); 32 | void interrupts_enableInterrupts(void); 33 | 34 | void interrupts_installEmptyIDT(void); 35 | void interrupts_installISRs(void); 36 | void interrupts_remap8259s(uint8 baseIRQ); 37 | void interrupts_setGate(uint8 Number, uint32 Address, uint16 GDTSelector, uint8 Flags); 38 | 39 | void interrupts_handler(struct Registers_S *Registers); 40 | 41 | //-- Private Definitions --// 42 | // Defines whether the interrupt table entry is valid. 43 | #define IDT_FLAG_PRESENT 0x80 44 | 45 | /** 46 | * Callers in ring 0 can call this interrupt (as well as hardware and exceptions). 47 | */ 48 | #define IDT_FLAG_RING0 0x00 49 | 50 | /** 51 | * Calls in rings 0 and 1 can call this interrupt (as well as hardware and exceptions). 52 | */ 53 | #define IDT_FLAG_RING1 0x20 54 | 55 | /** 56 | * Callers in rings 0 through 2 can call this interrupt (as well as hardware and exceptions). 57 | */ 58 | #define IDT_FLAG_RING2 0x40 59 | 60 | /** 61 | * Callers in rings 0 through 3 can call this interrupt (as well as hardware and exceptions). 62 | */ 63 | #define IDT_FLAG_RING3 0x60 64 | 65 | // Type of gate. 66 | #define IDT_FLAG_TASKGATE 0x05 67 | #define IDT_FLAG_INTGATE16 0x06 68 | #define IDT_FLAG_INTGATE32 0x0E 69 | #define IDT_FLAG_TRAPGATE16 0x07 70 | #define IDT_FLAG_TRAPGATE32 0x0F 71 | 72 | // Base address for 8529 PICs 73 | #define IDT_PIC1_BASEADDRESS 0x20 74 | #define IDT_PIC2_BASEADDRESS 0xA0 75 | 76 | //-- Interrupt Service Routine Stubs --// 77 | void interrupts_ISR_00(); 78 | void interrupts_ISR_01(); 79 | void interrupts_ISR_02(); 80 | void interrupts_ISR_03(); 81 | void interrupts_ISR_04(); 82 | void interrupts_ISR_05(); 83 | void interrupts_ISR_06(); 84 | void interrupts_ISR_07(); 85 | void interrupts_ISR_08(); 86 | void interrupts_ISR_09(); 87 | void interrupts_ISR_0A(); 88 | void interrupts_ISR_0B(); 89 | void interrupts_ISR_0C(); 90 | void interrupts_ISR_0D(); 91 | void interrupts_ISR_0E(); 92 | void interrupts_ISR_0F(); 93 | void interrupts_ISR_10(); 94 | void interrupts_ISR_11(); 95 | void interrupts_ISR_12(); 96 | void interrupts_ISR_13(); 97 | void interrupts_ISR_14(); 98 | void interrupts_ISR_15(); 99 | void interrupts_ISR_16(); 100 | void interrupts_ISR_17(); 101 | void interrupts_ISR_18(); 102 | void interrupts_ISR_19(); 103 | void interrupts_ISR_1A(); 104 | void interrupts_ISR_1B(); 105 | void interrupts_ISR_1C(); 106 | void interrupts_ISR_1D(); 107 | void interrupts_ISR_1E(); 108 | void interrupts_ISR_1F(); 109 | void interrupts_ISR_20(); 110 | void interrupts_ISR_21(); 111 | void interrupts_ISR_22(); 112 | void interrupts_ISR_23(); 113 | void interrupts_ISR_24(); 114 | void interrupts_ISR_25(); 115 | void interrupts_ISR_26(); 116 | void interrupts_ISR_27(); 117 | void interrupts_ISR_28(); 118 | void interrupts_ISR_29(); 119 | void interrupts_ISR_2A(); 120 | void interrupts_ISR_2B(); 121 | void interrupts_ISR_2C(); 122 | void interrupts_ISR_2D(); 123 | void interrupts_ISR_2E(); 124 | void interrupts_ISR_2F(); 125 | void interrupts_ISR_30(); 126 | void interrupts_ISR_31(); 127 | void interrupts_ISR_32(); 128 | void interrupts_ISR_33(); 129 | void interrupts_ISR_34(); 130 | void interrupts_ISR_35(); 131 | void interrupts_ISR_36(); 132 | void interrupts_ISR_37(); 133 | void interrupts_ISR_38(); 134 | void interrupts_ISR_39(); 135 | void interrupts_ISR_3A(); 136 | void interrupts_ISR_3B(); 137 | void interrupts_ISR_3C(); 138 | void interrupts_ISR_3D(); 139 | void interrupts_ISR_3E(); 140 | void interrupts_ISR_3F(); 141 | void interrupts_ISR_40(); 142 | void interrupts_ISR_41(); 143 | void interrupts_ISR_42(); 144 | void interrupts_ISR_43(); 145 | void interrupts_ISR_44(); 146 | void interrupts_ISR_45(); 147 | void interrupts_ISR_46(); 148 | void interrupts_ISR_47(); 149 | void interrupts_ISR_48(); 150 | void interrupts_ISR_49(); 151 | void interrupts_ISR_4A(); 152 | void interrupts_ISR_4B(); 153 | void interrupts_ISR_4C(); 154 | void interrupts_ISR_4D(); 155 | void interrupts_ISR_4E(); 156 | void interrupts_ISR_4F(); 157 | void interrupts_ISR_50(); 158 | void interrupts_ISR_51(); 159 | void interrupts_ISR_52(); 160 | void interrupts_ISR_53(); 161 | void interrupts_ISR_54(); 162 | void interrupts_ISR_55(); 163 | void interrupts_ISR_56(); 164 | void interrupts_ISR_57(); 165 | void interrupts_ISR_58(); 166 | void interrupts_ISR_59(); 167 | void interrupts_ISR_5A(); 168 | void interrupts_ISR_5B(); 169 | void interrupts_ISR_5C(); 170 | void interrupts_ISR_5D(); 171 | void interrupts_ISR_5E(); 172 | void interrupts_ISR_5F(); 173 | void interrupts_ISR_60(); 174 | void interrupts_ISR_61(); 175 | void interrupts_ISR_62(); 176 | void interrupts_ISR_63(); 177 | void interrupts_ISR_64(); 178 | void interrupts_ISR_65(); 179 | void interrupts_ISR_66(); 180 | void interrupts_ISR_67(); 181 | void interrupts_ISR_68(); 182 | void interrupts_ISR_69(); 183 | void interrupts_ISR_6A(); 184 | void interrupts_ISR_6B(); 185 | void interrupts_ISR_6C(); 186 | void interrupts_ISR_6D(); 187 | void interrupts_ISR_6E(); 188 | void interrupts_ISR_6F(); 189 | void interrupts_ISR_70(); 190 | void interrupts_ISR_71(); 191 | void interrupts_ISR_72(); 192 | void interrupts_ISR_73(); 193 | void interrupts_ISR_74(); 194 | void interrupts_ISR_75(); 195 | void interrupts_ISR_76(); 196 | void interrupts_ISR_77(); 197 | void interrupts_ISR_78(); 198 | void interrupts_ISR_79(); 199 | void interrupts_ISR_7A(); 200 | void interrupts_ISR_7B(); 201 | void interrupts_ISR_7C(); 202 | void interrupts_ISR_7D(); 203 | void interrupts_ISR_7E(); 204 | void interrupts_ISR_7F(); 205 | 206 | #ifdef __cplusplus 207 | } 208 | #endif 209 | 210 | #endif // INCLUDES_INTERRUPTS_H_ 211 | -------------------------------------------------------------------------------- /Source/drivers/atiRage128.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Driver for ATI Rage 128 Series Graphics Cards. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | * 6 | * 2D Capabilities: 7 | * [X] 2D Framebuffer 8 | * [-] Double Buffering 9 | * [-] Hardware Sprites (Cursor) 10 | * [-] Hardware Copy and Fill (Blitting) 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | VideoMode* atiRage128_currentMode = NULL; 19 | 20 | deviceTree_Entry* atiRage128_device = NULL; 21 | 22 | 23 | #define ATIRAGE128_REGOFFSET_CRTC_GEN_CTRL 0x0050 24 | 25 | #define ATIRAGE128_REGOFFSET_CRTC_H_TOTAL_DISP 0x0200 26 | #define ATIRAGE128_REGOFFSET_CRTC_H_SYNC_STRT_WID 0x0204 27 | #define ATIRAGE128_REGOFFSET_CRTC_V_TOTAL_DISP 0x0208 28 | #define ATIRAGE128_REGOFFSET_CRTC_V_SYNC_STRT_WID 0x020C 29 | 30 | /// @brief Where in video memory the cursor can be found. 31 | #define ATIRAGE128_REGOFFSET_CUR_OFFSET 0x260 32 | 33 | #define ATIRAGE128_REGOFFSET_CUR_HORZ_VERT_POSN 0x264 34 | #define ATIRAGE128_REGOFFSET_CUR_CLR0 0x26C 35 | #define ATIRAGE128_REGOFFSET_CUR_CLR1 0x270 36 | 37 | /** 38 | * Register offset for CRTC_PITCH. Stores the number of bytes per display line. 39 | */ 40 | #define ATIRAGE128_REGOFFSET_CRTC_PITCH 0x022C / 4 41 | 42 | void atiRage128_writeRegister(uint32 registerOffset, uint32 data); 43 | 44 | const VideoMode videoMode1024x768 = { 45 | "1024x768 @ 60Hz, 32bpp", 46 | 1024, 768, 47 | 24, 136, 160, 0, 48 | 3, 6, 29, 0, 49 | 65000000, 32 50 | }; 51 | 52 | const VideoMode videoMode640x480 = { 53 | "640x480 @ 60Hz, 32bpp", 54 | 640, 480, 55 | 16, 96, 48, 0, 56 | 10, 2, 33, 0, 57 | 25175000, 32 58 | }; 59 | 60 | deviceTree_Entry* atiRage128_initialise(pciBus_Entry* pciDetails) { 61 | uint32 bus = pciDetails->bus; 62 | uint32 slot = pciDetails->slot; 63 | uint32 function = pciDetails->function; 64 | PageDirectory* pg = memoryManager_getCurrentPageDirectory(); 65 | 66 | uint32 bar0 = pci_getBar(bus, slot, function, 0) & 0xFFFFFFF0; 67 | uint32 bar0size = pci_getBarSize(bus, slot, function, 0); 68 | // TODO(JackScottAU): check the size of the memory BAR and allocate more pages if needed. 69 | uint32 bar2 = pci_getBar(bus, slot, function, 2) & 0xFFFFFFFC; 70 | memoryManager_mapPhysicalMemoryPage(pg, (void*)0xFFC00000, (void*)bar0, 1024); // Framebuffer MM 71 | memoryManager_mapPhysicalMemoryPage(pg, (void*)0xFFBFC000, (void*)bar2, 4); // Register MMIO 72 | debug(LOGLEVEL_INFO, "ATI RAGE 128 BAR0: %h", bar0); 73 | debug(LOGLEVEL_INFO, "ATI RAGE 128 BAR2: %h", bar2); 74 | 75 | // BAR0 : MEM_BASE 76 | // BAR1 : IO_BASE (mirror of first part of reg_base) 77 | // BAR2 : REG_BASE 78 | 79 | // can now read registers. 80 | volatile uint32* regs = (uint32*) 0xFFBFC000; 81 | 82 | 83 | deviceTree_Entry* device = deviceTree_createDevice("ATI Rage 128 Display Adapter", DEVICETREE_TYPE_PCI, pciDetails); 84 | 85 | uint32 irq = pci_readConfigurationRegister(pciDetails->bus, pciDetails->slot, pciDetails->function, 0x3C) & 0x000000FF; 86 | 87 | int resourceCount = 2; 88 | 89 | if (irq > 0) { 90 | resourceCount = 3; 91 | } 92 | 93 | device->Resources = memoryManager_allocate(sizeof(DeviceResource) * resourceCount); 94 | device->ResourceCount = resourceCount; 95 | 96 | device->Resources[0].Type = DEVICE_RESOURCETYPE_MEM; 97 | device->Resources[0].StartAddress = bar0; 98 | device->Resources[0].Length = pci_getBarSize(bus, slot, function, 0); 99 | device->Resources[1].Type = DEVICE_RESOURCETYPE_MEM; 100 | device->Resources[1].StartAddress = bar2; 101 | device->Resources[1].Length = 0x4000; // 16KiB 102 | 103 | if (irq > 0) { 104 | device->Resources[2].Type = DEVICE_RESOURCETYPE_IRQ; 105 | device->Resources[2].Flags = irq; 106 | } 107 | 108 | deviceTree_Entry* monitor = deviceTree_createDevice("Generic Monitor", DEVICETREE_TYPE_OTHER, 0); 109 | 110 | deviceTree_addChild(device, monitor); 111 | 112 | atiRage128_device = device; 113 | 114 | // Pick our video mode. 115 | VideoMode* mode = &videoMode1024x768; 116 | 117 | atiRage128_setMode(mode); 118 | 119 | /* 120 | // fill cursor memnory with something, even if it makes no sense 121 | uint32* fbmem = (uint32*) 0xFFF00000; // skip 3meg 122 | for (int i = 0; i < 64 * 64; i++) { 123 | fbmem[i] = 0x8888CCCC + i; 124 | } 125 | 126 | regs[ATIRAGE128_REGOFFSET_CUR_OFFSET] = 0x300000; // 3mb 127 | regs[ATIRAGE128_REGOFFSET_CUR_HORZ_VERT_POSN] = 220 << 16 | 300; // pos 128 | regs[ATIRAGE128_REGOFFSET_CUR_CLR0] = 220 << 16 | 300 | 120 << 8; 129 | regs[ATIRAGE128_REGOFFSET_CUR_CLR1] = 120 << 16 | 200 | 80 << 8;*/ 130 | 131 | return device; 132 | } 133 | 134 | void atiRage128_setMode(VideoMode* mode) { 135 | 136 | volatile uint32* regs = (uint32*) 0xFFBFC000; 137 | 138 | uint32 hSyncStart = (mode->hRes + mode->hFront) / 8; 139 | uint32 hSyncWidth = mode->hSync / 8; 140 | uint32 hTotal = (mode->hRes + mode->hFront + mode->hSync + mode->hBack) / 8; 141 | uint32 hEnd = (mode->hRes / 8) -1; 142 | 143 | uint32 vSyncStart = (mode->vRes + mode->vFront); 144 | uint32 vSyncWidth = mode->vSync; 145 | uint32 vTotal = (mode->vRes + mode->vFront + mode->vSync + mode->vBack); 146 | uint32 vEnd = (mode->vRes) -1; 147 | 148 | uint8 hSyncPol = 1 - mode->hSyncPolarity; 149 | uint8 vSyncPol = 1 - mode->vSyncPolarity; 150 | 151 | uint32 depth = 0; 152 | 153 | switch (mode->depth) { 154 | case 15: 155 | depth = 3; 156 | break; 157 | 158 | case 32: 159 | depth = 6; 160 | break; 161 | 162 | default: 163 | depth = 6; 164 | } 165 | 166 | atiRage128_writeRegister(ATIRAGE128_REGOFFSET_CRTC_H_TOTAL_DISP, hTotal | (hEnd << 16)); 167 | 168 | atiRage128_writeRegister(ATIRAGE128_REGOFFSET_CRTC_V_TOTAL_DISP, vTotal | (vEnd << 16)); 169 | 170 | atiRage128_writeRegister(ATIRAGE128_REGOFFSET_CRTC_H_SYNC_STRT_WID, (hSyncStart << 3) | (hSyncWidth << 16) | (hSyncPol << 23)); // QEMU does not use this register at all. 171 | 172 | atiRage128_writeRegister(ATIRAGE128_REGOFFSET_CRTC_V_SYNC_STRT_WID, (vSyncStart) | (vSyncWidth << 16) | (vSyncPol << 23)); // QEMU deso not use this register at all. 173 | 174 | // Number of characters the screen is wide. 175 | atiRage128_writeRegister(ATIRAGE128_REGOFFSET_CRTC_PITCH, (mode->hRes) / 8); 176 | 177 | // Enable, 32bpp, extended. 178 | atiRage128_writeRegister(ATIRAGE128_REGOFFSET_CRTC_GEN_CTRL, (depth << 8) | (1 << 24) | (1 << 25)); 179 | 180 | // TODO(JackScottAU): pixel clock, memory clock 181 | 182 | atiRage128_currentMode = mode; 183 | } 184 | 185 | void atiRage128_writeRegister(uint32 registerOffset, uint32 data) { 186 | volatile uint32* regs = (uint32*) 0xFFBFC000; 187 | debug(LOGLEVEL_DEBUG, "ATIRAGE128: Writing data %h to register %h.", data, registerOffset); 188 | 189 | regs[registerOffset / 4] = data; // divide by 4 because a uint32 is 4 bytes. 190 | } 191 | 192 | void atiRage128_dumpCursorPos() { 193 | uint32* regs = (uint32*) 0xFFBFC000; 194 | debug(LOGLEVEL_DEBUG, "ATI CUR_HORZ_VERT_POSN: %h", regs[ATIRAGE128_REGOFFSET_CUR_HORZ_VERT_POSN]); 195 | } 196 | 197 | Canvas* atiRage128_getCanvas() { 198 | // Check to make sure a mode has been set. 199 | if (atiRage128_currentMode == NULL) { 200 | debug(LOGLEVEL_ERROR, "No video mode has been set, cannot create a canvas."); 201 | return NULL; 202 | } 203 | 204 | Canvas* canvas = (Canvas*)memoryManager_allocate(sizeof(Canvas)); 205 | canvas->framebuffer = (void*)0xFFC00000; 206 | canvas->height = atiRage128_currentMode->vRes; 207 | canvas->width = atiRage128_currentMode->hRes; 208 | 209 | return canvas; 210 | } 211 | -------------------------------------------------------------------------------- /Source/GUI/TextConsole.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | TextConsole::TextConsole(Canvas* canvas, ScreenFont* font, uint32 x, uint32 y, uint16 rows, uint16 columns) { 9 | this->x = x; 10 | this->y = y; 11 | this->rows = rows; 12 | this->columns = columns; 13 | this->currentColumn = 0; 14 | this->currentRow = 0; 15 | this->canvas = canvas; 16 | this->font = font; 17 | this->colour = 0x00FFFFFFF; // white. 18 | this->backcolour = 0x0; //black 19 | // do stuff. 20 | 21 | this->characterBuffer = (char**)memoryManager_allocate(sizeof(char*) * rows); 22 | this->foreColourBuffer = (uint32**)memoryManager_allocate(sizeof(uint32*) * rows); 23 | this->backColourBuffer = (uint32**)memoryManager_allocate(sizeof(uint32*) * rows); 24 | for(int i = 0; i < rows; i++){ 25 | this->characterBuffer[i] = (char*)memoryManager_allocate(sizeof(char) * columns); 26 | this->foreColourBuffer[i] = (uint32*)memoryManager_allocate(sizeof(uint32) * columns); 27 | this->backColourBuffer[i] = (uint32*)memoryManager_allocate(sizeof(uint32) * columns); 28 | memset(this->characterBuffer[i], ' ', columns); 29 | } 30 | 31 | Redraw(); 32 | 33 | // Set aside 128 characters for CSI parameter storage. 34 | csiParameters = (char*)memoryManager_allocate(128); 35 | isInCSI = false; 36 | } 37 | 38 | void TextConsole::PutChar(char c) { 39 | if (isInCSI) { 40 | HandleControlSequenceIntroducer(c); 41 | return; 42 | } 43 | 44 | 45 | switch(c) { 46 | case 0x1B: // ANSI Escape Code Handling 47 | isInCSI = true; 48 | memset(csiParameters, 0, sizeof(char) * 128); 49 | break; 50 | 51 | case 0x08: // Backspace 52 | if (currentColumn != 0) { 53 | currentColumn--; 54 | } 55 | 56 | this->characterBuffer[this->currentRow][this->currentColumn] = ' '; 57 | canvas_drawRect(canvas, x + (currentColumn * 8), y + (currentRow * 16), 8, 16, backcolour); 58 | break; 59 | 60 | case 0x09: // Tab 61 | currentColumn = (currentColumn + 8) & ~(8 - 1); 62 | break; 63 | 64 | case '\n': 65 | currentColumn = 0; 66 | currentRow++; 67 | break; 68 | 69 | case '\r': 70 | currentColumn = 0; 71 | break; 72 | 73 | default: 74 | //printable character. 75 | this->characterBuffer[this->currentRow][this->currentColumn] = c; 76 | this->foreColourBuffer[this->currentRow][this->currentColumn] = colour; 77 | this->backColourBuffer[this->currentRow][this->currentColumn] = backcolour; 78 | screenfont_drawChar(canvas, font, x + (currentColumn * 8), y + (currentRow * 16), this->colour, c); 79 | 80 | currentColumn++; 81 | 82 | if(currentColumn >= columns) { 83 | currentColumn = 0; 84 | currentRow++; 85 | } 86 | break; 87 | } 88 | 89 | if (currentRow >= rows) { 90 | debug(LOGLEVEL_TRACE, "Need to scrooll."); 91 | Scroll(); 92 | } 93 | 94 | debug(LOGLEVEL_TRACE, "Framebuffer row = %d, Framebuffer column = %d", this->currentRow, this->currentColumn); 95 | } 96 | 97 | void TextConsole::HandleControlSequenceIntroducer(char c) { 98 | // if we get a command, execute that and remove isInCSI. 99 | // else store characters into parameter string. 100 | 101 | int i = 0; 102 | 103 | switch (c) { 104 | case 'm': 105 | HandleSelectGraphicsRendition(); 106 | isInCSI = false; 107 | return; 108 | 109 | default: 110 | while (csiParameters[i] != 0) { 111 | i++; 112 | // stream_printf(serial_writeChar, "CSI(%d): %d\n", i, csiParameters[i]); 113 | } 114 | 115 | // now we are somewhere to store our parameter bit. 116 | csiParameters[i] = c; 117 | csiParameters[i+1] = 0; 118 | // serial_writeString("CSI: "); 119 | // serial_writeLine(csiParameters); 120 | return; 121 | } 122 | } 123 | 124 | /** When given a parameter string, */ 125 | void TextConsole::HandleSelectGraphicsRendition() { 126 | int i = 0; 127 | while (csiParameters[i] != '\0') { 128 | if (csiParameters[i] == '[' || csiParameters[i] == ';') { 129 | int sgr = string_parseInt(csiParameters + i + 1); 130 | 131 | DecodeSGR(sgr); 132 | } 133 | 134 | i++; 135 | } 136 | } 137 | 138 | void TextConsole::DecodeSGR(uint32 parameter) { 139 | switch (parameter) { 140 | case 0: 141 | colour = 0x00FFFFFF; 142 | backcolour = 0x00000000; 143 | break; 144 | 145 | case 30: 146 | colour = 0x00000000; 147 | break; 148 | 149 | case 31: 150 | colour = 0x00FF0000; 151 | break; 152 | 153 | case 32: 154 | colour = 0x0000FF00; 155 | break; 156 | 157 | case 33: 158 | colour = 0x00F08000; 159 | break; 160 | 161 | case 34: 162 | colour = 0x000000FF; 163 | break; 164 | 165 | case 35: 166 | colour = 0x00FF00FF; 167 | break; 168 | 169 | case 36: 170 | colour = 0x00008080; 171 | break; 172 | 173 | case 37: 174 | colour = 0x00808080; 175 | break; 176 | 177 | case 40: 178 | backcolour = 0x00000000; 179 | break; 180 | 181 | case 41: 182 | backcolour = 0x00FF0000; 183 | break; 184 | 185 | case 42: 186 | backcolour = 0x0000FF00; 187 | break; 188 | 189 | case 43: 190 | backcolour = 0x00F08000; 191 | break; 192 | 193 | case 44: 194 | backcolour = 0x000000FF; 195 | break; 196 | 197 | case 45: 198 | backcolour = 0x00FF00FF; 199 | break; 200 | 201 | case 46: 202 | backcolour = 0x00008080; 203 | break; 204 | 205 | case 47: 206 | backcolour = 0x00808080; 207 | break; 208 | } 209 | } 210 | 211 | 212 | void TextConsole::HandleUIEvent(GuiEvent* eventData) { 213 | 214 | } 215 | 216 | void TextConsole::Scroll() { 217 | debug(LOGLEVEL_TRACE, "Scrolling..."); 218 | // save address of top row. 219 | char* oldTopRow = characterBuffer[0]; 220 | uint32* oldTopRowF = foreColourBuffer[0]; 221 | uint32* oldTopRowB = backColourBuffer[0]; 222 | 223 | // move every row up one except first row 224 | for(int i = 1; i < rows; i++) { 225 | characterBuffer[i-1] = characterBuffer[i]; 226 | foreColourBuffer[i-1] = foreColourBuffer[i]; 227 | backColourBuffer[i-1] = backColourBuffer[i]; 228 | } 229 | // set last row as old first row. 230 | characterBuffer[rows - 1] = oldTopRow; 231 | foreColourBuffer[rows - 1] = oldTopRowF; 232 | backColourBuffer[rows - 1] = oldTopRowB; 233 | 234 | // empty new last row 235 | for(int i = 0; i < columns; i++) { 236 | characterBuffer[rows - 1][i] = ' '; 237 | } 238 | 239 | currentRow--; 240 | 241 | // redraw 242 | Redraw(); 243 | } 244 | 245 | void TextConsole::Redraw() { 246 | canvas_drawRect(canvas, x, y, x + (columns * 8), y + (rows * 16), backcolour); 247 | 248 | for(int row = 0; row < rows; row++) { 249 | for(int column = 0; column < columns; column++) { 250 | uint32 foreColour = foreColourBuffer[row][column]; 251 | uint32 backColour = backColourBuffer[row][column]; 252 | 253 | if(backcolour != backColour) { 254 | // draw a square where we need one. 255 | canvas_drawRect(canvas, x + (column * 8), y + (row * 16), 8, 16, backColour); 256 | } 257 | 258 | screenfont_drawChar(canvas, font, x + (column * 8), y + (row * 16), foreColour, characterBuffer[row][column]); 259 | } 260 | } 261 | 262 | debug(LOGLEVEL_TRACE, "Redrawed."); 263 | } 264 | -------------------------------------------------------------------------------- /Source/drivers/piixide.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Driver for PCI IDE Controllers. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define ATA_REGISTER_IO_LBALOW 3 14 | #define ATA_REGISTER_IO_LBAMID 4 15 | #define ATA_REGISTER_IO_LBAHIGH 5 16 | 17 | /** 18 | * The drive number (bit 4), whether to use CHS or LBA (our OS only supports LBA) (bit 6), and the top four bits of LBA (bits 0-3). 19 | */ 20 | #define ATA_REGISTEROFFSET_IO_DRIVE 6 21 | 22 | #define ATA_REGISTEROFFSET_IO_STATUS 7 23 | 24 | #define ATA_REGISTEROFFSET_CONTROL_STATUS 0 25 | #define ATA_REGISTEROFFSET_CONTROL_SELECT 1 26 | 27 | #define ATA_DRIVE_MASTER (0 << 4) 28 | #define ATA_DRIVE_SLAVE (1 << 4) 29 | 30 | void piixide_probeChannel(deviceTree_Entry* channelDevice); 31 | void piixide_softwareReset(deviceTree_Entry* channelDevice); 32 | void piixide_selectDrive(deviceTree_Entry* channelDevice, uint8 driveNumber); 33 | void piixide_wait(deviceTree_Entry* channelDevice); 34 | void piixide_waitForReady(deviceTree_Entry* channelDevice); 35 | 36 | deviceTree_Entry* piixide_decodeDriveSignature(uint32 cl, uint32 ch); 37 | 38 | deviceTree_Entry* piixide_decodeDriveSignature(uint32 cl, uint32 ch) { 39 | if (cl == 0x14 && ch == 0xEB) { 40 | return deviceTree_createDevice("PATA CD-ROM Drive?!", DEVICETREE_TYPE_OTHER, NULL); 41 | } 42 | 43 | if (cl == 0x69 && ch == 0x96) { 44 | return deviceTree_createDevice("SATA CD-ROM Drive?!", DEVICETREE_TYPE_OTHER, NULL); 45 | } 46 | 47 | if (cl == 0 && ch == 0) { 48 | return deviceTree_createDevice("PATA HDD Drive?!", DEVICETREE_TYPE_OTHER, NULL); 49 | } 50 | 51 | if (cl == 0x3c && ch == 0xc3) { 52 | return deviceTree_createDevice("SATA HDD Drive?!", DEVICETREE_TYPE_OTHER, NULL); 53 | } 54 | 55 | return NULL; 56 | } 57 | 58 | deviceTree_Entry* piixide_initialise(pciBus_Entry* pciDetails) { 59 | uint32 bus = pciDetails->bus; 60 | uint32 slot = pciDetails->slot; 61 | uint32 function = pciDetails->function; 62 | 63 | deviceTree_Entry* device = deviceTree_createDevice("Generic PCI IDE Controller", DEVICETREE_TYPE_PCI, pciDetails); 64 | 65 | // check programming interface byte: if bits set then we use BARs, otherwise use default addresses. 66 | uint32 classLong = pci_readConfigurationRegister(bus, slot, function, 0x08); 67 | uint8 programmingInterface = (classLong > 8) & 0xFF; 68 | 69 | // check programming interface byte: 70 | if (programmingInterface & 2) { 71 | // todo 72 | } 73 | 74 | // I/O bar for bus mastering. 75 | uint32 bar4 = pci_getBar(bus, slot, function, 4) & 0xFFFFFFFC; 76 | uint32 bar4size = pci_getBarSize(bus, slot, function, 4); 77 | 78 | device->ResourceCount = 0; 79 | 80 | deviceTree_Entry* channel1 = deviceTree_createDevice("IDE Channel", DEVICETREE_TYPE_OTHER, NULL); 81 | deviceTree_addChild(device, channel1); 82 | channel1->ResourceCount = 4; 83 | channel1->Resources = memoryManager_allocate(sizeof(DeviceResource) * 4); 84 | channel1->Resources[0].Type = DEVICE_RESOURCETYPE_IO; 85 | channel1->Resources[0].StartAddress = 0x1F0; 86 | channel1->Resources[0].Length = 8; 87 | 88 | channel1->Resources[1].Type = DEVICE_RESOURCETYPE_IO; 89 | channel1->Resources[1].StartAddress = 0x3F6; 90 | channel1->Resources[1].Length = 2; 91 | 92 | channel1->Resources[2].Type = DEVICE_RESOURCETYPE_IO; 93 | channel1->Resources[2].StartAddress = bar4; 94 | channel1->Resources[2].Length = bar4size / 2; 95 | 96 | channel1->Resources[3].Type = DEVICE_RESOURCETYPE_IRQ; 97 | channel1->Resources[3].Flags = 14; 98 | 99 | deviceTree_Entry* channel2 = deviceTree_createDevice("IDE Channel", DEVICETREE_TYPE_OTHER, NULL); 100 | deviceTree_addChild(device, channel2); 101 | channel2->ResourceCount = 4; 102 | channel2->Resources = memoryManager_allocate(sizeof(DeviceResource) * 4); 103 | channel2->Resources[0].Type = DEVICE_RESOURCETYPE_IO; 104 | channel2->Resources[0].StartAddress = 0x170; 105 | channel2->Resources[0].Length = 8; 106 | 107 | channel2->Resources[1].Type = DEVICE_RESOURCETYPE_IO; 108 | channel2->Resources[1].StartAddress = 0x376; 109 | channel2->Resources[1].Length = 2; 110 | 111 | channel2->Resources[2].Type = DEVICE_RESOURCETYPE_IO; 112 | channel2->Resources[2].StartAddress = bar4 + 8; 113 | channel2->Resources[2].Length = bar4size / 2; 114 | 115 | channel2->Resources[3].Type = DEVICE_RESOURCETYPE_IRQ; 116 | channel2->Resources[3].Flags = 15; 117 | 118 | // (ports 0x1F0-0x1F7, 0x3F6-0x3F7, IRQ14). 119 | // (ports 0x170-0x177, 0x376-0x377, IRQ15). 120 | /* 121 | channels[ATA_PRIMARY ].base = (BAR0 & 0xFFFFFFFC) + 0x1F0 * (!BAR0); 122 | channels[ATA_PRIMARY ].ctrl = (BAR1 & 0xFFFFFFFC) + 0x3F6 * (!BAR1); 123 | channels[ATA_SECONDARY].base = (BAR2 & 0xFFFFFFFC) + 0x170 * (!BAR2); 124 | channels[ATA_SECONDARY].ctrl = (BAR3 & 0xFFFFFFFC) + 0x376 * (!BAR3); 125 | channels[ATA_PRIMARY ].bmide = (BAR4 & 0xFFFFFFFC) + 0; // Bus Master IDE 126 | channels[ATA_SECONDARY].bmide = (BAR4 & 0xFFFFFFFC) + 8; // Bus Master IDE*/ 127 | 128 | /* on Primary bus: ctrl->base =0x1F0, ctrl->dev_ctl =0x3F6. REG_CYL_LO=4, REG_CYL_HI=5, REG_DEVSEL=6 */ 129 | 130 | piixide_probeChannel(channel1); 131 | 132 | piixide_probeChannel(channel2); 133 | 134 | return device; 135 | } 136 | 137 | void piixide_probeChannel(deviceTree_Entry* channelDevice) { 138 | uint16 ioBase = channelDevice->Resources[0].StartAddress; 139 | 140 | piixide_softwareReset(channelDevice); 141 | 142 | piixide_selectDrive(channelDevice, ATA_DRIVE_MASTER); 143 | 144 | uint8 status = portIO_read8(ioBase + 7); 145 | 146 | if(status != 0xFF && status != 0x00) { 147 | piixide_waitForReady(channelDevice); 148 | 149 | unsigned cl=portIO_read8(ioBase + 4); /* get the "signature bytes" */ 150 | unsigned ch=portIO_read8(ioBase + 5); 151 | 152 | deviceTree_addChild(channelDevice, piixide_decodeDriveSignature(cl, ch)); 153 | 154 | debug(LOGLEVEL_DEBUG, "ATA Master Status: %h", status); 155 | debug(LOGLEVEL_DEBUG, "ATA Master: %h %h", cl, ch); 156 | } 157 | 158 | 159 | 160 | piixide_selectDrive(channelDevice, ATA_DRIVE_SLAVE); 161 | 162 | status = portIO_read8(ioBase + 7); 163 | 164 | if(status != 0xFF && status != 0x00) { 165 | piixide_waitForReady(channelDevice); 166 | uint8 cl=portIO_read8(ioBase + 4); /* get the "signature bytes" */ 167 | uint8 ch=portIO_read8(ioBase + 5); 168 | 169 | debug(LOGLEVEL_DEBUG, "ATA Slave Status: %h", status); 170 | debug(LOGLEVEL_DEBUG, "ATA Slave: %h %h", cl, ch); 171 | 172 | deviceTree_addChild(channelDevice, piixide_decodeDriveSignature(cl, ch)); 173 | } 174 | } 175 | 176 | void piixide_waitForReady(deviceTree_Entry* channelDevice) { 177 | // Read the Regular Status port until bit 7 (BSY, value = 0x80) clears, and bit 3 (DRQ, value = 8) sets -- or until bit 0 (ERR, value = 1) or bit 5 (DF, value = 0x20) sets. If neither error bit is set, the device is ready right then. 178 | bool notReady = true; 179 | uint16 ioBase = channelDevice->Resources[0].StartAddress; 180 | 181 | // 0xDO = 11010000 = BSY | RDY | SRV 182 | 183 | while(notReady) { 184 | uint8 status = portIO_read8(ioBase + 7); 185 | 186 | // Check if BUSY is set. 187 | if (status >> 7) { 188 | 189 | } else { 190 | notReady = false; 191 | } 192 | } 193 | } 194 | 195 | void piixide_selectDrive(deviceTree_Entry* channelDevice, uint8 driveNumber) { 196 | uint16 ioBase = channelDevice->Resources[0].StartAddress; 197 | 198 | /* waits until master drive is ready again */ 199 | portIO_write8(ioBase + ATA_REGISTEROFFSET_IO_DRIVE, 0xE0 | driveNumber); 200 | piixide_wait(channelDevice); 201 | } 202 | 203 | void piixide_softwareReset(deviceTree_Entry* channelDevice) { 204 | uint16 controlBase = channelDevice->Resources[1].StartAddress; 205 | 206 | // reset 207 | portIO_write8(controlBase + ATA_REGISTEROFFSET_CONTROL_STATUS, 4); // SRST 208 | piixide_wait(channelDevice); 209 | portIO_write8(controlBase + ATA_REGISTEROFFSET_CONTROL_STATUS, 0); // No software reset 210 | piixide_wait(channelDevice); 211 | } 212 | 213 | /// @brief We really only need to use this when selecting a drive, as the status update is immediate when not drive switching. 214 | /// @param channelDevice 215 | void piixide_wait(deviceTree_Entry* channelDevice) { 216 | uint16 controlBase = channelDevice->Resources[1].StartAddress; 217 | 218 | for (int i = 0; i < 16; i++) { 219 | portIO_read8(controlBase + ATA_REGISTEROFFSET_CONTROL_STATUS); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /Source/drivers/ps2controller.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Amethyst Operating System - Driver for 8042 PS/2 Controller. 3 | * Copyright 2024 Jack Scott . 4 | * Released under the terms of the ISC license. 5 | * 6 | * This code will initialise the controller, determine what devices are plugged into it, and call the drivers 7 | * responsible for initialising the connected devices. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #define PS2CONTROLLER_DATAPORT 0x60 21 | #define PS2CONTROLLER_CONTROLPORT 0x64 // reading = status register, writing = command register. 22 | 23 | #define PS2CONTROLLER_COMMAND_DISABLE_PORT1 0xAD 24 | #define PS2CONTROLLER_COMMAND_DISABLE_PORT2 0xA7 25 | #define PS2CONTROLLER_COMMAND_ENABLE_PORT1 0xAE 26 | #define PS2CONTROLLER_COMMAND_ENABLE_PORT2 0xA8 27 | 28 | #define PS2CONTROLLER_CONFIG_ENABLE_INTERRUPTS_PORT1 0x01 29 | #define PS2CONTROLLER_CONFIG_ENABLE_INTERRUPTS_PORT2 0x02 30 | #define PS2CONTROLLER_CONFIG_DISABLE_PORT1 0x10 31 | #define PS2CONTROLLER_CONFIG_DISABLE_PORT2 0x20 32 | 33 | void ps2controller_waitForRead(void); 34 | void ps2controller_waitForWrite(void); 35 | 36 | uint8 ps2controller_getConfigurationByte(void); 37 | void ps2controller_setConfigurationByte(uint8 configByte); 38 | 39 | void ps2controller_enableInterrupts(void); 40 | void ps2controller_disableInterrupts(void); 41 | 42 | uint8 ps2controller_getFirstIdentifyByte(uint8 channel); 43 | 44 | /** 45 | * 46 | */ 47 | deviceTree_Entry* ps2controller_initialise(void) { 48 | bool channel1Status = true; // By default, channel one is available. 49 | bool channel2Status = false; // By default, channel two is unavailable (assume a single-channel controller). 50 | 51 | // Resource: https://wiki.osdev.org/%228042%22_PS/2_Controller 52 | 53 | // Missing steps: 1 (usb init), 2 (determine controller exists) - these are done at the level above. 54 | 55 | // Step 1: Disable Devices & Flush Output (Read) Buffer 56 | portIO_write8(PS2CONTROLLER_CONTROLPORT, PS2CONTROLLER_COMMAND_DISABLE_PORT1); 57 | portIO_write8(PS2CONTROLLER_CONTROLPORT, PS2CONTROLLER_COMMAND_DISABLE_PORT2); 58 | portIO_read8(PS2CONTROLLER_DATAPORT); 59 | 60 | // Step 2: Perform Controller Self-Test 61 | portIO_write8(PS2CONTROLLER_CONTROLPORT, 0xAA); 62 | ps2controller_waitForRead(); 63 | if (portIO_read8(PS2CONTROLLER_DATAPORT) != 0x55) { 64 | debug(LOGLEVEL_ERROR, "PS/2 Controller failed self-test."); 65 | return NULL; 66 | } 67 | 68 | // Step 3: Determine if there are two channels. 69 | uint8 configByte = ps2controller_getConfigurationByte(); 70 | channel2Status = configByte & PS2CONTROLLER_CONFIG_DISABLE_PORT2; 71 | 72 | // Step 4: Perform Interface Tests 73 | if (channel1Status) { 74 | portIO_write8(PS2CONTROLLER_CONTROLPORT, 0xAB); 75 | ps2controller_waitForRead(); 76 | if (portIO_read8(PS2CONTROLLER_DATAPORT) != 0x00) { 77 | debug(LOGLEVEL_WARNING, "PS/2 Controller channel one failed interface test."); 78 | channel1Status = false; 79 | } 80 | } 81 | 82 | if (channel2Status) { 83 | portIO_write8(PS2CONTROLLER_CONTROLPORT, 0xA9); 84 | ps2controller_waitForRead(); 85 | if (portIO_read8(PS2CONTROLLER_DATAPORT) != 0x00) { 86 | debug(LOGLEVEL_WARNING, "PS/2 Controller channel two failed interface test."); 87 | channel2Status = false; 88 | } 89 | } 90 | 91 | // Step 5: Enable Devices. 92 | ps2controller_disableInterrupts(); 93 | if (channel1Status) { 94 | portIO_write8(PS2CONTROLLER_CONTROLPORT, PS2CONTROLLER_COMMAND_ENABLE_PORT1); 95 | } 96 | if (channel2Status) { 97 | portIO_write8(PS2CONTROLLER_CONTROLPORT, PS2CONTROLLER_COMMAND_ENABLE_PORT2); 98 | } 99 | 100 | // Step 6: Initialise child devices. 101 | deviceTree_Entry* parent = deviceTree_createDevice("PS/2 Controller", DEVICETREE_TYPE_OTHER, NULL); 102 | 103 | parent->ResourceCount = 1 + channel1Status + channel2Status; 104 | parent->Resources = memoryManager_allocate(sizeof(DeviceResource) * parent->ResourceCount); 105 | parent->Resources[0].Type = DEVICE_RESOURCETYPE_IO; 106 | parent->Resources[0].StartAddress = 0x60; 107 | parent->Resources[0].Length = 8; 108 | 109 | // TODO(JackScottAU): add code to detect what is plugged in where, so we could have two keyboards or two mice or mouse and keyboard swapped. 110 | 111 | if (channel1Status) { 112 | // Send identify command. 113 | uint8 identification = ps2controller_getFirstIdentifyByte(1); 114 | 115 | debug(LOGLEVEL_DEBUG, "Channel 1 Identification: %h", identification); 116 | 117 | deviceTree_addChild(parent, keyboard_initialise()); 118 | 119 | parent->Resources[1].Type = DEVICE_RESOURCETYPE_IRQ; 120 | parent->Resources[1].Flags = 1; 121 | } 122 | 123 | if (channel2Status) { 124 | // Send identify command. 125 | // Temporarily disable mouse so we can test on a computer that doesn't have a mouse! 126 | /* uint8 identification = ps2controller_getFirstIdentifyByte(1); 127 | 128 | debug(LOGLEVEL_DEBUG, "Channel 2 Identification: %h", identification); 129 | 130 | deviceTree_addChild(parent, mouse_initialise()); 131 | 132 | parent->Resources[2].Type = DEVICE_RESOURCETYPE_IRQ; 133 | parent->Resources[2].Flags = 12;*/ 134 | } 135 | 136 | ps2controller_enableInterrupts(); 137 | 138 | debug(LOGLEVEL_INFO, "PS/2 Controller initialisation complete."); 139 | 140 | return parent; 141 | } 142 | 143 | uint8 ps2controller_getFirstIdentifyByte(uint8 channel) { 144 | // Send the Disable Scanning command 0xF5 to the device 145 | ps2controller_sendByteToDevice(channel, 0xF5); 146 | 147 | // Wait for the device to send ACK back (0xFA) 148 | ps2controller_waitForRead(); 149 | ps2controller_receiveByteFromDevice(channel); 150 | 151 | // Send the Identify command 0xF2 to the device 152 | ps2controller_sendByteToDevice(channel, 0xF5); 153 | 154 | // Wait for the device to send ACK back (0xFA) 155 | ps2controller_waitForRead(); 156 | ps2controller_receiveByteFromDevice(channel); 157 | 158 | // Wait for the device to send up to 2 bytes of reply, with a time-out to determine when it's finished (e.g. in case it only sends 1 byte) 159 | ps2controller_waitForRead(); 160 | uint8 ident = ps2controller_receiveByteFromDevice(channel); 161 | if (ident > 128) { 162 | ps2controller_waitForRead(); 163 | ps2controller_receiveByteFromDevice(channel); 164 | } 165 | 166 | // Send the Enable Scanning command 0xF4 to the device 167 | ps2controller_sendByteToDevice(channel, 0xF4); 168 | ps2controller_waitForRead(); 169 | ps2controller_receiveByteFromDevice(channel); 170 | } 171 | 172 | void ps2controller_disableInterrupts() { 173 | debug(LOGLEVEL_DEBUG, "PS/2 Controller: Disabling interrupts..."); 174 | 175 | uint8 configByte = ps2controller_getConfigurationByte(); 176 | 177 | configByte = configByte & 0xF4; // Disable interrupts on both devices. 178 | 179 | ps2controller_setConfigurationByte(configByte); 180 | 181 | debug(LOGLEVEL_DEBUG, "PS/2 Controller: Disabled interrupts."); 182 | } 183 | 184 | void ps2controller_enableInterrupts() { 185 | debug(LOGLEVEL_DEBUG, "PS/2 Controller: Enabling interrupts..."); 186 | 187 | uint8 configByte = ps2controller_getConfigurationByte(); 188 | 189 | configByte = configByte | 0x03; // Enable interrupts on both devices. 190 | 191 | ps2controller_setConfigurationByte(configByte); 192 | 193 | debug(LOGLEVEL_DEBUG, "PS/2 Controller: Enabled interrupts."); 194 | } 195 | 196 | uint8 ps2controller_getConfigurationByte(void) { 197 | portIO_write8(PS2CONTROLLER_CONTROLPORT, 0x20); 198 | ps2controller_waitForRead(); 199 | uint8 configByte = portIO_read8(PS2CONTROLLER_DATAPORT); 200 | 201 | debug(LOGLEVEL_DEBUG, "PS/2 Controller: Got configuration byte, value = %h", configByte); 202 | 203 | return configByte; 204 | } 205 | 206 | void ps2controller_setConfigurationByte(uint8 configByte) { 207 | portIO_write8(PS2CONTROLLER_CONTROLPORT, 0x60); 208 | ps2controller_waitForWrite(); 209 | portIO_write8(PS2CONTROLLER_DATAPORT, configByte); 210 | 211 | debug(LOGLEVEL_DEBUG, "PS/2 Controller: Set configuration byte, value = %h", configByte); 212 | } 213 | 214 | void ps2controller_sendByteToDevice(uint8 channel, uint8 data) { 215 | if (channel == 2) { 216 | ps2controller_waitForWrite(); 217 | portIO_write8(PS2CONTROLLER_CONTROLPORT, 0xD4); // next command to channel two. 218 | } 219 | 220 | ps2controller_waitForWrite(); 221 | portIO_write8(PS2CONTROLLER_DATAPORT, data); 222 | } 223 | 224 | uint8 ps2controller_receiveByteFromDevice(uint8 channel) { 225 | ps2controller_waitForRead(); 226 | return portIO_read8(PS2CONTROLLER_DATAPORT); 227 | } 228 | 229 | /** 230 | * Waits until the PS/2 Controller data port can be succesfully read from. 231 | */ 232 | void ps2controller_waitForRead() { 233 | while (!portIO_read8(PS2CONTROLLER_CONTROLPORT) & 0x01) { } 234 | } 235 | 236 | /** 237 | * Waits until the PS/2 Controller data port can be succesfully written to. 238 | */ 239 | void ps2controller_waitForWrite() { 240 | while (portIO_read8(PS2CONTROLLER_CONTROLPORT) & 0x02) { } 241 | } 242 | -------------------------------------------------------------------------------- /Source/arch/x86_32/interrupts_stubs.S: -------------------------------------------------------------------------------- 1 | #Exceptions 2 | .global interrupts_ISR_00 3 | .global interrupts_ISR_01 4 | .global interrupts_ISR_02 5 | .global interrupts_ISR_03 6 | .global interrupts_ISR_04 7 | .global interrupts_ISR_05 8 | .global interrupts_ISR_06 9 | .global interrupts_ISR_07 10 | .global interrupts_ISR_08 11 | .global interrupts_ISR_09 12 | .global interrupts_ISR_0A 13 | .global interrupts_ISR_0B 14 | .global interrupts_ISR_0C 15 | .global interrupts_ISR_0D 16 | .global interrupts_ISR_0E 17 | .global interrupts_ISR_0F 18 | .global interrupts_ISR_10 19 | .global interrupts_ISR_11 20 | .global interrupts_ISR_12 21 | .global interrupts_ISR_13 22 | .global interrupts_ISR_14 23 | .global interrupts_ISR_15 24 | .global interrupts_ISR_16 25 | .global interrupts_ISR_17 26 | .global interrupts_ISR_18 27 | .global interrupts_ISR_19 28 | .global interrupts_ISR_1A 29 | .global interrupts_ISR_1B 30 | .global interrupts_ISR_1C 31 | .global interrupts_ISR_1D 32 | .global interrupts_ISR_1E 33 | .global interrupts_ISR_1F 34 | 35 | #IRQs 36 | .global interrupts_ISR_20 37 | .global interrupts_ISR_21 38 | .global interrupts_ISR_22 39 | .global interrupts_ISR_23 40 | .global interrupts_ISR_24 41 | .global interrupts_ISR_25 42 | .global interrupts_ISR_26 43 | .global interrupts_ISR_27 44 | .global interrupts_ISR_28 45 | .global interrupts_ISR_29 46 | .global interrupts_ISR_2A 47 | .global interrupts_ISR_2B 48 | .global interrupts_ISR_2C 49 | .global interrupts_ISR_2D 50 | .global interrupts_ISR_2E 51 | .global interrupts_ISR_2F 52 | 53 | .global interrupts_ISR_30 54 | .global interrupts_ISR_31 55 | .global interrupts_ISR_32 56 | .global interrupts_ISR_33 57 | .global interrupts_ISR_34 58 | .global interrupts_ISR_35 59 | .global interrupts_ISR_36 60 | .global interrupts_ISR_37 61 | .global interrupts_ISR_38 62 | .global interrupts_ISR_39 63 | .global interrupts_ISR_3A 64 | .global interrupts_ISR_3B 65 | .global interrupts_ISR_3C 66 | .global interrupts_ISR_3D 67 | .global interrupts_ISR_3E 68 | .global interrupts_ISR_3F 69 | 70 | # 0 Divide By Zero Exception 71 | interrupts_ISR_00: 72 | push $0 73 | push $0 74 | jmp interrupts_ISR_Stub 75 | 76 | # 1 Debug Exception 77 | interrupts_ISR_01: 78 | push $0 79 | push $1 80 | jmp interrupts_ISR_Stub 81 | 82 | # 2 Non Maskable Interrupt Exception 83 | interrupts_ISR_02: 84 | push $0 85 | push $2 86 | jmp interrupts_ISR_Stub 87 | 88 | # 3 Int 3 Exception 89 | interrupts_ISR_03: 90 | push $0 91 | push $3 92 | jmp interrupts_ISR_Stub 93 | 94 | # 4 INTO Exception 95 | interrupts_ISR_04: 96 | push $0 97 | push $4 98 | jmp interrupts_ISR_Stub 99 | 100 | # 5 Out of Bounds Exception 101 | interrupts_ISR_05: 102 | push $0 103 | push $5 104 | jmp interrupts_ISR_Stub 105 | 106 | # 6 Invalid Opcode Exception 107 | interrupts_ISR_06: 108 | push $0 109 | push $6 110 | jmp interrupts_ISR_Stub 111 | 112 | # 7 Coprocessor Not Available Exception 113 | interrupts_ISR_07: 114 | push $0 115 | push $7 116 | jmp interrupts_ISR_Stub 117 | 118 | # 8 Double Fault Exception (With Error Code!) 119 | interrupts_ISR_08: 120 | push $8 121 | jmp interrupts_ISR_Stub 122 | 123 | # 9 Coprocessor Segment Overrun Exception 124 | interrupts_ISR_09: 125 | push $0 126 | push $9 127 | jmp interrupts_ISR_Stub 128 | 129 | # 10: Bad TSS Exception (With Error Code!) 130 | interrupts_ISR_0A: 131 | push $10 132 | jmp interrupts_ISR_Stub 133 | 134 | # 11: Segment Not Present Exception (With Error Code!) 135 | interrupts_ISR_0B: 136 | push $11 137 | jmp interrupts_ISR_Stub 138 | 139 | # 12: Stack Fault Exception (With Error Code!) 140 | interrupts_ISR_0C: 141 | push $12 142 | jmp interrupts_ISR_Stub 143 | 144 | # 13: General Protection Fault Exception (With Error Code!) 145 | interrupts_ISR_0D: 146 | push $13 147 | jmp interrupts_ISR_Stub 148 | 149 | # 14: Page Fault Exception (With Error Code!) 150 | interrupts_ISR_0E: 151 | push $14 152 | jmp interrupts_ISR_Stub 153 | 154 | # 15: Reserved Exception 155 | interrupts_ISR_0F: 156 | push $0 157 | push $15 158 | jmp interrupts_ISR_Stub 159 | 160 | # 16: Floating Point Exception 161 | interrupts_ISR_10: 162 | push $0 163 | push $16 164 | jmp interrupts_ISR_Stub 165 | 166 | # 17: Alignment Check Exception 167 | interrupts_ISR_11: 168 | push $0 169 | push $17 170 | jmp interrupts_ISR_Stub 171 | 172 | # 18: Machine Check Exception 173 | interrupts_ISR_12: 174 | push $0 175 | push $18 176 | jmp interrupts_ISR_Stub 177 | 178 | # 19: Reserved 179 | interrupts_ISR_13: 180 | push $0 181 | push $19 182 | jmp interrupts_ISR_Stub 183 | 184 | # 20: Reserved 185 | interrupts_ISR_14: 186 | push $0 187 | push $20 188 | jmp interrupts_ISR_Stub 189 | 190 | # 21: Reserved 191 | interrupts_ISR_15: 192 | push $0 193 | push $21 194 | jmp interrupts_ISR_Stub 195 | 196 | # 22: Reserved 197 | interrupts_ISR_16: 198 | push $0 199 | push $22 200 | jmp interrupts_ISR_Stub 201 | 202 | # 23: Reserved 203 | interrupts_ISR_17: 204 | push $0 205 | push $23 206 | jmp interrupts_ISR_Stub 207 | 208 | # 24: Reserved 209 | interrupts_ISR_18: 210 | push $0 211 | push $24 212 | jmp interrupts_ISR_Stub 213 | 214 | # 25: Reserved 215 | interrupts_ISR_19: 216 | push $0 217 | push $25 218 | jmp interrupts_ISR_Stub 219 | 220 | # 26: Reserved 221 | interrupts_ISR_1A: 222 | push $0 223 | push $26 224 | jmp interrupts_ISR_Stub 225 | 226 | # 27: Reserved 227 | interrupts_ISR_1B: 228 | push $0 229 | push $27 230 | jmp interrupts_ISR_Stub 231 | 232 | # 28: Reserved 233 | interrupts_ISR_1C: 234 | push $0 235 | push $28 236 | jmp interrupts_ISR_Stub 237 | 238 | # 29: Reserved 239 | interrupts_ISR_1D: 240 | push $0 241 | push $29 242 | jmp interrupts_ISR_Stub 243 | 244 | # 30: Reserved 245 | interrupts_ISR_1E: 246 | push $0 247 | push $30 248 | jmp interrupts_ISR_Stub 249 | 250 | # 31: Reserved 251 | interrupts_ISR_1F: 252 | push $0 253 | push $31 254 | jmp interrupts_ISR_Stub 255 | 256 | 257 | interrupts_ISR_20: 258 | push $0 259 | push $0x20 260 | jmp interrupts_ISR_Stub 261 | 262 | interrupts_ISR_21: 263 | push $0 264 | push $0x21 265 | jmp interrupts_ISR_Stub 266 | 267 | interrupts_ISR_22: 268 | push $0 269 | push $0x22 270 | jmp interrupts_ISR_Stub 271 | 272 | interrupts_ISR_23: 273 | push $0 274 | push $0x23 275 | jmp interrupts_ISR_Stub 276 | 277 | interrupts_ISR_24: 278 | push $0 279 | push $0x24 280 | jmp interrupts_ISR_Stub 281 | 282 | interrupts_ISR_25: 283 | push $0 284 | push $0x25 285 | jmp interrupts_ISR_Stub 286 | 287 | interrupts_ISR_26: 288 | push $0 289 | push $0x26 290 | jmp interrupts_ISR_Stub 291 | 292 | interrupts_ISR_27: 293 | push $0 294 | push $0x27 295 | jmp interrupts_ISR_Stub 296 | 297 | interrupts_ISR_28: 298 | push $0 299 | push $0x28 300 | jmp interrupts_ISR_Stub 301 | 302 | interrupts_ISR_29: 303 | push $0 304 | push $0x29 305 | jmp interrupts_ISR_Stub 306 | 307 | interrupts_ISR_2A: 308 | push $0 309 | push $0x2A 310 | jmp interrupts_ISR_Stub 311 | 312 | interrupts_ISR_2B: 313 | push $0 314 | push $0x2B 315 | jmp interrupts_ISR_Stub 316 | 317 | interrupts_ISR_2C: 318 | push $0 319 | push $0x2C 320 | jmp interrupts_ISR_Stub 321 | 322 | interrupts_ISR_2D: 323 | push $0 324 | push $0x2D 325 | jmp interrupts_ISR_Stub 326 | 327 | interrupts_ISR_2E: 328 | push $0 329 | push $0x2E 330 | jmp interrupts_ISR_Stub 331 | 332 | interrupts_ISR_2F: 333 | push $0 334 | push $0x2F 335 | jmp interrupts_ISR_Stub 336 | 337 | interrupts_ISR_30: 338 | push $0 339 | push $0x30 340 | jmp interrupts_ISR_Stub 341 | 342 | interrupts_ISR_31: 343 | push $0 344 | push $0x31 345 | jmp interrupts_ISR_Stub 346 | 347 | interrupts_ISR_32: 348 | push $0 349 | push $0x32 350 | jmp interrupts_ISR_Stub 351 | 352 | interrupts_ISR_33: 353 | push $0 354 | push $0x33 355 | jmp interrupts_ISR_Stub 356 | 357 | interrupts_ISR_34: 358 | push $0 359 | push $0x34 360 | jmp interrupts_ISR_Stub 361 | 362 | interrupts_ISR_35: 363 | push $0 364 | push $0x35 365 | jmp interrupts_ISR_Stub 366 | 367 | interrupts_ISR_36: 368 | push $0 369 | push $0x36 370 | jmp interrupts_ISR_Stub 371 | 372 | interrupts_ISR_37: 373 | push $0 374 | push $0x37 375 | jmp interrupts_ISR_Stub 376 | 377 | interrupts_ISR_38: 378 | push $0 379 | push $0x38 380 | jmp interrupts_ISR_Stub 381 | 382 | interrupts_ISR_39: 383 | push $0 384 | push $0x39 385 | jmp interrupts_ISR_Stub 386 | 387 | interrupts_ISR_3A: 388 | push $0 389 | push $0x3A 390 | jmp interrupts_ISR_Stub 391 | 392 | interrupts_ISR_3B: 393 | push $0 394 | push $0x3B 395 | jmp interrupts_ISR_Stub 396 | 397 | interrupts_ISR_3C: 398 | push $0 399 | push $0x3C 400 | jmp interrupts_ISR_Stub 401 | 402 | interrupts_ISR_3D: 403 | push $0 404 | push $0x3D 405 | jmp interrupts_ISR_Stub 406 | 407 | interrupts_ISR_3E: 408 | push $0 409 | push $0x3E 410 | jmp interrupts_ISR_Stub 411 | 412 | interrupts_ISR_3F: 413 | push $0 414 | push $0x3F 415 | jmp interrupts_ISR_Stub 416 | 417 | # We call a C function in here. We need to let the assembler know 418 | # that '_fault_handler' exists in another file 419 | .extern interrupts_handler 420 | 421 | # This is our common ISR stub. It saves the processor state, sets 422 | # up for kernel mode segments, calls the C-level fault handler, 423 | # and finally restores the stack frame. 424 | interrupts_ISR_Stub: 425 | pusha 426 | push %ds 427 | push %es 428 | push %fs 429 | push %gs 430 | movw $0x10,%ax 431 | movw %ax,%ds 432 | movw %ax,%es 433 | movw %ax,%fs 434 | movw %ax,%gs 435 | movl %esp,%eax 436 | pushl %eax 437 | movl $interrupts_handler, %eax 438 | call *%eax 439 | popl %eax 440 | popl %gs 441 | popl %fs 442 | popl %es 443 | popl %ds 444 | popa 445 | addl $8,%esp # Compensate for the two values pushed in the stubs above. 446 | iret 447 | --------------------------------------------------------------------------------