Files
Asuro/src/arch/x86/arch.x86.util.pas
T
Aaron 663bfbb0e4 storage: lifecycle watch system, PFSFormatParams, vol busy/format flags, diskutil improvements
- Add TStorageLifecycleWatch with watch_lifecycle/unwatch_lifecycle API (up to 32 watchers)
- Add notify_lifecycle to broadcast mount/unmount/format events to registered callbacks
- Replace raw puint32 config in format_volume with typed PFSFormatParams
- Add volume_is_busy, volume_is_formatting, volume_set_mounted helpers
- Add lifecycleFlags field to TStorage_Volume for tracking busy/formatting state
- Expand app.diskutil with improved commands and volume state reporting
- Minor improvements to core.util, arch.x86.util, proc.mgr, VFS, and flatfs
- Remove obsolete driver.storage.fs.fat32.old.pas
2026-03-16 23:03:43 +00:00

442 lines
8.5 KiB
ObjectPascal

// Copyright 2021 Kieron Morris
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{
arch.x86.util - x86 I/O and low-level CPU utilities.
Port I/O (outb/inb/outw/inw/outl/inl), CLI/STI, TSC reads,
BSOD, halt, reset, and anything requiring inline x86 assembly.
@author(Kieron Morris <[email protected]>)
@author(Aaron Hance <[email protected]>)
}
unit arch.x86.util;
{$ASMMODE intel}
interface
uses
arch.x86.bda;
function INTE : boolean;
procedure CLI();
procedure STI();
procedure GPF();
procedure outb(port : uint16; val : uint8);
procedure outw(port : uint16; val : uint16);
procedure outl(port : uint16; val : uint32);
function inb(port : uint16) : uint8;
function inw(port : uint16) : uint16;
function inl(port : uint16) : uint32;
procedure io_wait;
procedure __SSE_128_memcpy(source : uint32; dest : uint32);
procedure __REP_MOVSB_memcpy(source : uint32; dest : uint32; size : uint32);
procedure halt_and_catch_fire();
procedure halt_and_dont_catch_fire();
procedure psleep(t : uint16);
procedure sleep(seconds : uint32);
function get16bitcounter : uint16;
function get32bitcounter : uint32;
function get64bitcounter : uint64;
function getTSC : uint64;
function div6432(dividend : uint64; divisor : uint32) : uint64;
procedure resetSystem();
function getESP : uint32;
function RolDWord(AValue : uint32; Dist : uint8) : uint32;
function RorDWord(AValue : uint32; Dist : uint8) : uint32;
function MsSinceSystemBoot : uint64;
var
endptr : uint32; external name '__end';
stack : uint32; external name 'KERNEL_STACK';
implementation
uses
arch.x86.cpu,
arch.x86.isr.types,
driver.timer.rtc,
driver.io.serial;
function MsSinceSystemBoot : uint64;
begin
MsSinceSystemBoot:= div6432(getTSC, (CPUID.ClockSpeed.Hz div 1000));
end;
function div6432(dividend : uint64; divisor : uint32) : uint64;
var
d0, d4 : uint32;
r0, r4 : uint32;
begin
d4:= dividend SHR 32;
d0:= dividend AND $FFFFFFFF;
asm
PUSHAD
xor edx, edx
mov eax, d4
div divisor
mov r4, eax
mov eax, d0
div divisor
mov r0, eax
POPAD
end;
div6432:= (r0 SHL 32) OR r4;
end;
procedure __SSE_128_memcpy(source : uint32; dest : uint32); assembler;
asm
MOV EAX, Source
MOVAPS XMM1, [EAX]
MOV EAX, Dest
MOVAPS [EAX], XMM1
end;
procedure __REP_MOVSB_memcpy(source : uint32; dest : uint32; size : uint32); assembler;
asm
push esi
push edi
cld
mov esi, source
mov edi, dest
mov ecx, size
rep movsb
pop edi
pop esi
end;
function getESP : uint32;
var
tmp: uint32;
begin
asm
MOV tmp, ESP
end;
getESP := tmp;
end;
procedure sleep1;
var
DateTimeStart, DateTimeEnd : TDateTime;
begin
DateTimeStart:= getDateTime;
DateTimeEnd:= DateTimeStart;
while DateTimeStart.seconds = DateTimeEnd.seconds do begin
DateTimeEnd:= getDateTime;
end;
end;
procedure sleep(seconds : uint32);
var
i : uint32;
begin
for i:=1 to seconds do begin
sleep1;
end;
end;
function INTE : boolean;
var
flags : uint32;
begin
asm
PUSH EAX
PUSHF
POP EAX
MOV flags, EAX
POP EAX
end;
INTE:= (flags AND (1 SHL 9)) > 0;
end;
procedure io_wait;
var
port : uint8;
val : uint8;
begin
port:= $80;
val:= 0;
asm
PUSH EAX
PUSH EDX
MOV DX, port
MOV AL, val
OUT DX, AL
POP EDX
POP EAX
end;
end;
procedure CLI(); assembler; nostackframe;
asm
CLI
end;
procedure STI(); assembler; nostackframe;
asm
STI
end;
procedure GPF(); assembler;
asm
INT 13
end;
procedure psleep(t : uint16);
var
t1, t2 : uint16;
spin : uint32;
begin
t1 := BDA^.Ticks;
{ Busy-spin briefly to give the tick counter a chance to advance.
If after ~50 000 iterations the counter hasn't moved, the timer
ISR isn't installed yet — fall back to a rough CPU busy-wait
(~1 ms per requested tick at typical Bochs/VBox speed). }
spin := 0;
t2 := t1;
while t2 = t1 do begin
spin := spin + 1;
if spin > 50000 then begin
{ Timer not running — approximate delay with busy loop.
Each outer iteration ≈ a few µs; 50000 * t gives a
very rough ms-scale delay that is good enough for the
hardware settle times that call psleep. }
for spin := 1 to uint32(t) * 50000 do
asm nop end;
exit;
end;
t2 := BDA^.Ticks;
end;
{ Timer is running — use real ticks }
t1 := BDA^.Ticks;
while t2 - t1 < t do begin
t2 := BDA^.Ticks;
if t2 < t1 then break; { tick counter wrapped }
end;
end;
procedure outl(port : uint16; val : uint32); [public, alias: 'util_outl'];
begin
asm
PUSH EAX
PUSH EDX
MOV DX, port
MOV EAX, val
OUT DX, EAX
POP EDX
POP EAX
end;
io_wait;
end;
procedure outw(port : uint16; val : uint16); [public, alias: 'util_outw'];
begin
asm
PUSH EAX
PUSH EDX
MOV DX, port
MOV AX, val
OUT DX, AX
POP EDX
POP EAX
end;
io_wait;
end;
procedure outb(port : uint16; val : uint8); [public, alias: 'util_outb'];
begin
asm
PUSH EAX
PUSH EDX
MOV DX, port
MOV AL, val
OUT DX, AL
POP EDX
POP EAX
end;
io_wait;
end;
procedure halt_and_catch_fire(); [public, alias: 'util_halt_and_catch_fire'];
begin
asm
cli
hlt
end;
end;
procedure halt_and_dont_catch_fire(); [public, alias: 'util_halt_and_dont_catch_fire'];
begin
while true do begin
end;
end;
function inl(port : uint16) : uint32; [public, alias: 'util_inl'];
var
tmp : uint32;
begin
asm
PUSH EAX
PUSH EDX
MOV DX, port
IN EAX, DX
MOV tmp, EAX
POP EDX
POP EAX
end;
inl := tmp;
io_wait;
end;
function inw(port : uint16) : uint16; [public, alias: 'util_inw'];
var
tmp : uint16;
begin
asm
PUSH EAX
PUSH EDX
MOV DX, port
IN AX, DX
MOV tmp, AX
POP EDX
POP EAX
end;
inw := tmp;
io_wait;
end;
function inb(port : uint16) : uint8; [public, alias: 'util_inb'];
var
tmp : uint8;
begin
asm
PUSH EAX
PUSH EDX
MOV DX, port
IN AL, DX
MOV tmp, AL
POP EDX
POP EAX
end;
inb := tmp;
io_wait;
end;
function get16bitcounter : uint16;
begin
get16bitcounter:= arch.x86.bda.Counters.c16;
end;
function get32bitcounter : uint32;
begin
get32bitcounter:= arch.x86.bda.Counters.c32;
end;
function get64bitcounter : uint64;
begin
get64bitcounter:= arch.x86.bda.Counters.c64;
end;
function getTSC : uint64;
var
hi, lo : uint32;
begin
asm
PUSH EAX
PUSH EDX
RDTSC
MOV hi, EDX
MOV lo, EAX
POP EDX
POP EAX
end;
getTSC:= (hi SHL 32) OR lo;
end;
function RolDWord(AValue : uint32; Dist : uint8) : uint32;
var
result : uint32;
i : uint8;
begin
result:= AValue;
asm
PUSH EAX
end;
for i:=0 to Dist-1 do begin
asm
MOV EAX, result
ROL EAX, 1
MOV result, EAX
end;
end;
asm
POP EAX
end;
RolDWord:= result;
end;
function RorDWord(AValue : uint32; Dist : uint8) : uint32;
var
result : uint32;
i : uint8;
begin
result:= AValue;
asm
PUSH EAX
end;
for i:=0 to Dist-1 do begin
asm
MOV EAX, result
ROR EAX, 1
MOV result, EAX
end;
end;
asm
POP EAX
end;
RorDWord:= result;
end;
procedure resetSystem();
var
good : uint8;
begin
CLI;
good:= $02;
while (good AND $02) > 0 do good:= inb($64);
outb($64, $FE);
halt_and_catch_fire;
end;
end.