Add core.fmt.json — a complete RFC 8259 JSON library with recursive-descent
parser, serializer, builders, dot/bracket path accessors, and full cleanup.
Supports all 6 JSON value types plus JSON_ERROR for parse error reporting.
Numbers stored as Double via x87 FPU. Unicode \uXXXX escapes handled
(ASCII range emitted, non-ASCII replaced with '?').
Supporting changes:
- core.strings: add int64ToString, doubleToStr (scientific notation for
very large/small values, fixed-point for normal range, trailing zero
trimming), plus unit tests for both
- asuro.pas: wire core.fmt.json.UnitTest into the boot test suite
- mkdocs.yml: add JSON nav entry under Formats
- doc/src/core/fmt/core.fmt.json.md: full API documentation
- doc/src/core/core.strings.md: document int64ToString and doubleToStr
- doc/planning/json.md: implementation plan and design spec
- doc/planning/webgpu.md: WebGPU-over-WASM design plan (software
rasterizer backend for WASM apps importing webgpu.h)
The Drone runner environment does not have `pip` or `mkdocs` on PATH
even when installed via the Docker image. Use `pip3` explicitly and
invoke mkdocs as `python3 -m mkdocs` to ensure reliable execution.
Also add --break-system-packages to the fallback install.
Generate comprehensive documentation for all 173 source units and
integrate mkdocs-material into the build toolchain. Documentation
covers kernel core, x86 architecture, drivers, applications, and
all subsystems with a tabbed navigation structure.
- Add mkdocs.yml with material theme, mermaid support, and tabbed nav
- Add doc/index.md landing page
- Generate doc/src/ markdown for every Pascal unit in src/
- Reorganize planning docs into doc/planning/
- Add compat, lvglh, and toolchain doc sections
- Update Dockerfile to install python3 and mkdocs-material (pinned <2)
- Replace pasdoc-based compile_docs.sh with mkdocs build --strict
- Add compile_docs.sh to the main build pipeline in compile.sh
- Add deploy-docs step in .drone.yml with host volume mount to nginx
- Add site/.gitkeep for build output directory
Introduce core.panic, a new architecture-agnostic kernel panic engine
with a pre-allocated LVGL BSOD screen. The screen and all widgets are
created at init time so that zero LVGL allocation occurs at panic time.
Layout features:
- Top banner with decoded teapot TGA image (128x128) and title/subtitle
- Two-column content area with dark semi-transparent cards:
- Left: Fault Details, CPU Registers, Process Info, System Info
- Right: Call Stack (full height)
- Pastel red background (#7A2828), monospace text for data sections
Key changes:
- Add core.panic.pas with pre-built BSOD screen, register/trace/system
info formatting, hex conversion helpers, and syslog fallback
- Add arch.x86.panic.pas as the x86 bridge: packs interrupt registers
into a TRegisterSnapshot and calls core.panic.panic()
- Simplify all 19 x86 fault handlers to one-liner panic calls
- Remove legacy BSOD code from arch.x86.util.pas
- Add teapot.tga (256x256) embedded via splash_tga.asm
- Add LVGL bindings: lv_refr_now, lv_image_create, lv_image_set_src,
lv_image_set_scale, lv_image_set_inner_align, and style helpers
- Wire up panic init and test command in asuro.pas
- ramdrive: rewrite from registerDrive callbacks to TFilesystem/TStorage_Volume
hooks (rd_Read, rd_ReadDir, rd_ReadOffset, rd_Identify) and mount via
vfs.mountVolume('/disk/ram', ...). Remove open-slot tracking; VFS now
manages file handles through the FD table.
- vfs: fix PathValid for otDRIVE nodes — split relative path into parent
directory + leaf name, list the parent via readDirCallback, and check
each entry's type so individual files are correctly identified as pvFile
instead of always returning pvDirectory or pvInvalid.
- desktop: replace static array[0..MAX_PROGRAMS-1] program registry with
a dynamic PLinkedListBase. Lazy-init on first registerProgram call.
Remove prog_count := 0 from init so programs registered before
desktop.init (e.g. notepad, diskutil) are no longer wiped.
- filedispatch, wasmrunner: add storagetypes to uses, remove stale Lock
parameter from vfs.OpenFile calls.
- progmanager: clean up duplicate uses clauses and remove invalid
kernel.bsod registration.
- compile_wasuro.sh was copying files to the /code directory which
is not where drone builds from, fixed to use $(pwd) and relative
directories based on the root.
- Add compile_wasuro.sh to sparse-clone Wasuro src/wasm from gitea
(develop branch) with caching to skip redundant pulls
- Update compile.sh to include wasuro build step in pipeline
- Update compile_sources.sh to dynamically discover -Fu paths via
find across src/ and wasuro/ directories
- Extend system.pas with FPC 3.2.2 compilerprocs required by Wasuro:
- 64-bit signed/unsigned div and mod (fpc_div_int64, fpc_mod_int64,
fpc_div_qword, fpc_mod_qword) with correct i386 parameter mapping
- 64-bit multiply (fpc_mul_int64) via three 32-bit MUL instructions
- Float-to-integer intrinsics (Trunc, Round) using x87 FPU
- Memory allocation compilerprocs (fpc_getmem, fpc_freemem)
delegating to kernel kalloc/kfree
- Exception handling stubs (reraise, raiseexception, catches, doneexception)
- setjmp/longjmp for exception address stack
- Fix sInt64 type alias (was longint/32-bit, now int64/64-bit)
- Add serial debug helpers (COM1 $3F8) for bare-metal tracing
- Wire up Wasuro in kernel.pas: init WASM VM, set I/O callback to
syslog, and run all 1103 Wasuro tests (0 failures)
- Update .gitignore to exclude wasuro/ cache directory
- Remove diskcmd, diskutil, notepad, partcmd, volcmd init calls and
uses from kernel.pas — these are programs, not kernel subsystems.
- Move their initialization into progmanager.init() alongside the
other baked-in programs (md5sum, base64_prog, dhclient, etc.).
- Remove diskcmd, diskutil, notepad from kernel's uses clause.
- Add a splash.update() call for the auto-mount volumes step.
- Suppress FPC warnings in compile_sources.sh (-v0e instead of -v0ew).
- New splash unit (src/prog/splash.pas) displays a centered logo,
progress bar and status label during kernel boot. Manually drives
LVGL rendering + video.Flush since gfxd isn't running yet.
- Embed img/asuro.tga into the kernel via NASM incbin stub
(src/stub/splash_tga.asm), assembled in compile_stub.sh.
- Build an lv_image_dsc_t at runtime from the decoded TGA pixel data
and display it at 50% scale using lv_image_set_scale(128).
- Move LVGL init earlier in kernel.pas (right after video enable) so
the splash screen is available before VFS/drivers/network init.
- Sprinkle splash.update() calls throughout kmain to show boot
progress (VFS → drivers → buses → network → tests → desktop).
- Fix targa.pas parser: rename Magic→IDLength, remove erroneous Data
pointer field, compute pixel offset correctly (buffer+18+IDLength),
advance source pointer per pixel, handle bottom-to-top origin flip.
- Unit tests moved before desktop.init; splash.teardown called at 100%
just before handing off to the desktop environment.
- desktop: Resolution/Graphics fields now read from video frontbuffer
and gpu.activeDriverName instead of static multiboot/hardcoded values
- gpu: markAvailable sets ActiveName when no driver is active yet, so
the initial VBE boot driver is reported before any setMode call
- kernel: whitespace cleanup
PS/2 Mouse:
- Fix ISR spin-blocking by reading port $60 directly with inb() instead
of calling mouse_read(), which uses mouse_wait_long (100k iterations)
and would stall the interrupt handler waiting for the next packet byte.
Timer (PIT):
- Increase PIT frequency from ~1024 Hz to ~8 kHz (divisor 1193 -> 149)
for more granular timer ticks.
Graphics Refresh:
- Replace timer-hook-driven rendering (driver/timers/graphicsrefresh)
with a dedicated 'gfxd' process that runs a continuous render loop,
keeping the vCPU active to avoid NEM/Hyper-V pause-loop descheduling.
- Move uidebug from driver/video/ to prog/.
- Update FPS calculation to account for 8 kHz tick rate.
USB Hotplug:
- Replace timer-hook-driven polling (driver/timers/usbhotplug) with a
dedicated 'usbd' daemon process that sleeps 1s between poll cycles.
- Add bounds check in usbcore.usb_check_hotplug for empty HC list.
Cleanup:
- Remove old scheduler unit (superseded by processmanager/contextswitcher).
- Remove tss unit and tss.init() call from kernel.
- Remove unused syslog import from contextswitcher.
- Reduce BASE_QUANTUM from 8 to 5.
Replace the blocking main render loop in kernel.pas with two new
timer-hooked units that are driven by the 1024Hz PIT interrupt:
- graphicsrefresh: calls desktop.update, uidebug.update, lvgl_handler
and video.Flush every 4 ticks (~256 FPS target).
- usbhotplug: calls usbcore.usb_check_hotplug every 1024 ticks (~1Hz).
The kernel main procedure now registers both timer hooks and enters an
idle STI/HLT loop (kernel.yield), allowing the CPU to sleep between
interrupts instead of busy-spinning.
Also removes the unused myUserLandFunction and reorders early syslog
init calls for clarity.
- Track enumerated devices per-HC via linked list (hc^.Devices)
- Add usb_clear_halt() for endpoint stall recovery (CLEAR_FEATURE)
- Add usb_remove_device() to clean up and free device records
- Add get_hc() for indexed host controller access
- Implement 'USB' terminal command showing HC info, connected devices,
speeds, class descriptors, and endpoint details
The PS/2 keyboard callback hardcoded is_down_code := true for every
scancode, so key releases were never reported. This caused keys to
repeat endlessly until the next key was pressed.
In PS/2 scancode set 1, bit 7 indicates a break (release) code.
The callback now:
- Detects break codes via (scancode AND $80)
- Masks to base code (scancode AND $7F) for key matrix lookup
- Sets is_down_code := false for break codes
- Updates modifier state (shift/ctrl/alt) before building the
key event, so modifier releases take effect immediately
Files changed:
- ps2_keyboard.pas: rewrite callback to handle make/break codes
Replace main-loop polling (poll_all, poll_keyboards, poll_mice) with
hardware interrupt handlers for all four USB host controller drivers.
Each HC driver (XHCI, EHCI, OHCI, UHCI) now:
- Registers an ISR on its PCI interrupt line via isrmanager
- Enables hardware interrupts after HC initialization
- Acknowledges interrupt status in the ISR before calling poll,
preventing interrupt storms on level-triggered PCI lines
- Uses a PollBusy re-entrancy guard to prevent ISR/inline poll races
usbcore gains a completion hook system (register_completion_hook /
fire_completion_hooks) so HID drivers (keyboard, mouse) are notified
directly from the ISR after transfer completion.
Files changed:
- usbcore.pas: completion hook infrastructure
- XHCI.pas: ISR, enable_interrupts, submit ordering fix
- EHCI.pas: ISR, enable_interrupts, status ack in ISR
- OHCI.pas: ISR, enable_interrupts, status ack in ISR
- UHCI.pas: ISR, enable_interrupts, status ack in ISR
- usb_keyboard.pas: register completion hook
- usb_mouse.pas: register completion hook
- New units: usbtypes, usbcore, usbhub, UHCI, OHCI, usb_keyboard,
usb_mouse
- usbtypes: shared USB descriptors, constants, HC abstraction,
aligned alloc, unit tests
- usb_keyboard: USB HID boot keyboard driver with HID-to-ASCII
translation, polling, unit tests
- usb_mouse: USB HID boot mouse driver with delta/scroll/button
processing, polling, unit tests
HID Layer Restructure:
- keyboard.pas: stripped to abstract API (hook + reportKeyEvent),
no PS/2 dependency
- mouse.pas: absorbed mousestate.pas, now owns
position/buttons/scroll/hooks directly
- mousestate.pas: deleted (merged into mouse.pas)
- New ps2_keyboard.pas: PS/2 scan code translation,
calls keyboard.reportKeyEvent
- New ps2_mouse.pas: PS/2 packet handling, calls
mouse.setMousePos/fireMouseEvent
LVGL Integration:
- lvgl.pas: updated to use mouse.pas directly (was mousestate),
improved keyboard input handling with proper key held/release tracking
- vterminal.pas: removed serial debug logging, dropped serial dependency
Kernel:
- kernel.pas: added USB units, USB polling in main loop, unit test
calls, ps2_keyboard/ps2_mouse init
When size=0 was passed to memset() or memcpy(), the loop bound
`0 to size-1` underflowed to $FFFFFFFF, iterating ~4 billion times
and causing an immediate page fault.
Added `if size = 0 then exit` guards to both memset() and memcpy()
in util.pas.
Also guarded all `for i:=0 to X-1` loops in strings.pas where X
could be 0 on empty string inputs:
- stringToUpper: wrap loop in `if stringSize > 0`
- stringToLower: wrap loop in `if stringSize > 0`
- stringEquals: early exit when both sizes are 0
- stringToInt: early exit when string is empty
Additionally fixed all previously identified bugs in strings.pas:
- hexStringToInt: guard against empty string underflow
- stringConcat: cache stringSize calls to avoid redundant O(n) scans
- stringTrim: clamp length to source string size
- stringSub: bounds check start/size against source length
- stringReplace: complete rewrite - was using stringEquals() for
substring matching (only matched at string end), dropped prefix
chars, had dangling assignment overwriting valid result with
uninitialized pointer, and had unused variable
- stringIndexOf: rewritten to use new stringMatchAt() helper for
correct substring matching
- stringContains: simplified to delegate to fixed stringIndexOf(),
also fixing uint32 underflow when sub was empty
- stringToInt: removed always-true `v >= 0` check on uint32
- intToString: implemented (was a stub returning ' ')
- Added stringMatchAt() helper for prefix comparison
- Added comprehensive UnitTest procedure with 77 assertions
including adversarial empty/nil/boundary inputs
refactor(stdio): replace console/terminal command system with buffer-based stdio
- Introduced stdio.pas as the unified command I/O layer.
- All 31 command procedures across 15 files now receive stdin_buf,
stdout_buf, and stderr_buf (POutBuf) instead of a single outbuf.
- vterminal.pas creates all three buffers per command invocation and
displays both stdout and stderr.
- Deleted console.pas, terminal.pas, and 9 obsolete windowing programs
(shell, splash, edit, memview, themer, netlog, vmlog, vmstate, udpcat).
- Migrated all registerCommand calls to stdio, rewrote vterminal
processCommand to use stdio.findCommand, and added proper error
routing to stderr for invalid params, bad paths, and system errors.
refactor(syslog): replace console logging with serial-backed syslog
- Introduced syslog.pas for kernel-level logging over serial/dev.
- Migrated all boot logging in kernel.pas from console.outputln/
writestringln to syslog.logln/writestringln.
- Converted 19 fault handlers, hashmap, lists, scheduler, tracer, net,
udp, and all driver init paths to use syslog.
- Rewrote BSOD in util.pas (50+ calls) from console to syslog.
- Removed CONSOLE_SLOW_REDRAW, TRGB565, TRGB565Pair, and HWND
from system.pas.
fix(keyboard): add ring buffer and proper press/release cycling for LVGL input
- Replaced the single last_key/key_pressed slot in lvgl.pas with a
16-entry ring buffer (kb_buf, kb_head, kb_tail) to prevent ISR
overwrites when typing fast.
- Added a kb_pending_release flag so each keystroke gets a
full PRESSED->RELEASED cycle before the next key is consumed —
LVGL requires this pairing and was silently dropping intermediate
keys when receiving consecutive PRESSED events without
intervening releases.
feat(vterminal): use Hack monospace font for terminal output
- Added Hack Regular 14px (hack_14.c) as a custom LVGL font.
- Removed the .static_bitmap field from the generated font file
(not present in LVGL 9.2 lv_font_t struct).
- Declared the hack_14 external symbol in lvgl.pas and switched
vterminal's text label from lv_font_montserrat_14 to hack_14
for proper monospace terminal rendering.
XGETBV/XSETBV require CR4 bit 18 (OSXSAVE) to be set before execution,
otherwise the CPU raises #UD (invalid opcode). Hyper-V enforces this
strictly; VirtualBox was silently tolerating it.
- Check both AVX and XSAVE CPUID flags before attempting AVX enable
- Set CR4.OSXSAVE (bit 18) before issuing XGETBV/XSETBV
- If XSAVE is not exposed by the hypervisor, AVX enable is skipped
gracefully instead of faulting
- Add mouse_wait_long() with 100,000 iteration timeout for init-time
i8042 controller commands (mouse_write/mouse_read/load). The original
mouse_wait() stays at 100 iterations for ISR-safe use in main().
- In load(): use mouse_wait_long() for all direct port I/O ($64/$60)
and clear CCB bit 5 (AND NOT $20) to ensure the mouse clock is not
disabled — required for Hyper-V which enforces this strictly.
- Replace Console.getConsoleProperties^.Width/Height with
video.frontBufferWidth/Height for mouse bounds clamping. The old
console properties are zero when console.init() is not called,
which locked the cursor to (0,0). Add 'video' to uses clause.
The LVGL flush callback assumed 32bpp (4-byte stride, direct pixel
copy), causing corrupted output on hypervisors that fall back to
16bpp modes (e.g. HyperV at 1600x1200x16).
- lvgl.pas: Make lvgl_flush_cb BPP-aware; add ARGB8888->RGB565
conversion path for 16bpp, keep direct copy for 32bpp. Replace
static lv_buf1 array (hardcoded 1600px wide) with kalloc'd buffer
sized to actual screen width.
- doublebuffer.pas: Fix allocateBackBuffer size from (W*H*BPP) to
(W*H*BPP) div 8 to get correct byte count.
- vesa.pas: Account for BPP in allocateVESAFrameBuffer page range
calculation to avoid under-mapping at higher BPP/resolutions.
- Removed the initialization of console in kernel.pas as this will
cause issues on some systems that don't expect to need to allocate the
textmode buffer.
- Updated `Virtualbox-Wrapper.ps1` to include `Get-Job | Stop-Job | Remove-Job -Force` at the end of execution, as the spawning of jobs was causing stale PowerShell processes to build up over time.
Formatting was off for the message posted to Discord, it also triggered at the start of the pipeline.
- Fixed formatting with an attempt to use newlines.
- Added a depends_on `compile`.
- Fixed a bug within `dhcp.pas` - `processPacket_OFFER` in which the client was responding with the client IP value within the DHCP header was incorrectly being filled out with the IP being requested & this value was then being used within the REQUESTED_IP_ADDRESS option. Corrected this to fill out the client IP with the currently configured IP, which will be NULL (0.0.0.0) on boot, and whatever is issued thereafter.
- Cascaded the change to use the currently configured IP as opposed to a NULL IP to any other functions that were calling `copyIPv4(@NULL_IP[0], @packetCtx^.IP.Source[0])`.
- Allowed `processPacket_OFFER` to process packets addressed to the BROADCAST MAC (WHY COULDN'T IT DO THIS ALREADY?!).
- CI/CD Pipeline now working & tested.
- Commits to all branches will trigger the pipeline in DroneCI.
- Commits to master will trigger the resulting ISO artefact to be uploaded to Gitea as a Package.
# Conflicts:
# .drone.yml
- CI/CD Pipeline now working & tested.
- Commits to all branches will trigger the pipeline in DroneCI.
- Commits to master will trigger the resulting ISO artefact to be uploaded to Gitea as a Package.
- Further attempts to fix permission issues within Drone when exporting the Asuro.iso artefact
- Switched to `alpine/git` instead of `curl` image, as git didn't exist under the `curl` image.
- Removed the debug `exit 1` from `compile_stub.sh`
- Improved `compile.sh` to use runOrFail in a more suitable way, correctly passing through failure messages.