OS w pascalu cz. 3 - IDT, PIC, PIT, Floppy i FAT (TMP)

lukasz1235
Jest to stara wersja tej części serii. Jak widać usunięcie jej to dla moderatorów wielki problem.

Wstęp

Po napisaniu kodu zawartego w tej części nasz system będzie miał obsługę przerwań, timera, dyskietki i części FATa.
1 Wstęp
2 start.asm
3 kernel.pas
4 idt.pas
5 int.asm
6 pit.pas
7 floppy.pas
8 fat.pas
9 console.pas
10 keyboard.pas
11 system.pas
12 kernel.ld
13 Makefile
14 Zakończenie

start.asm

[BITS 32]
[SECTION .text]
EXTERN code,bss,end
mboot:
dd 0x1BADB002
dd 0x10001
dd -(0x1BADB002+0x10001)
dd bss
dd end
dd _start 
 
GLOBAL _start
 
EXTERN StartKernel
 
L6:
 jmp L6
 
_start:
call StartKernel
 
[SECTION .bss]
kstack: resd 1024
 
[SECTION .data]

kernel.pas

{$ASMMODE INTEL}
 
unit kernel;
 
interface
 
uses console, keyboard, idt, pit, floppy;
 
procedure StartKernel;
 
implementation
 
procedure StartKernel;[public,alias:'StartKernel'];
begin
    init_idt;
    init_pic;
    init_timer(100);
 
    shell;
    asm
        sti
    end;
    FloppyReset(0);
    asm
        @@a:
            jmp @@a
    end;
end;
 
end.

Tutaj inicjujemy IDT, PIC, Timer (wartość w nawiasie to częstotliwość), włączamy przerwania sprzętowe, resetujemy FDC i wprowadzamy system w nieskończoną pętlę.

idt.pas

{$ASMMODE INTEL}
 
unit idt;
 
interface
 
const
    PIC1 = $20;
    PIC2 = $A0;
    ICW1 = $11;
    ICW4 = $01;
    TRAP_GATE = $8F00;
 
type
    TIDTEntry = packed record
        offset_0: word;
        selector: word;
        typ: word;
        offset_16: word;
    end;
 
    TIDTPtr = packed record
        limit : word;
        base : LongWord;
    end;
 
var
    IDTTables : array[0..255] of TIDTEntry;
    IDTPtr : TIDTPtr;
 
procedure setup_int(i: integer; p_hand:dword; typ:word);
procedure init_idt;
procedure init_pic;
procedure keys_isr; external 'int.o';
procedure unknown_interrupt_isr; external 'int.o';
procedure timer_isr; external 'int.o';
procedure floppy_isr; external 'int.o';
 
implementation
 
procedure setup_int(i: integer; p_hand:dword; typ:word); [public, alias : 'setup_int'];
begin
    IDTTables[i].offset_0 := p_hand;
    IDTTables[i].selector := $08;
    IDTTables[i].typ := typ;
    IDTTables[i].offset_16 := (p_hand >> 16);
end;
 
procedure init_idt; [public, alias : 'init_idt'];
var
    i:integer;
begin
    for i:=0 to 255 do
    begin
        setup_int(i, DWord(@unknown_interrupt_isr), TRAP_GATE);
    end;
    setup_int(32, DWord(@timer_isr), TRAP_GATE);
    setup_int(33, DWord(@keys_isr), TRAP_GATE);
    setup_int(38, DWord(@floppy_isr), TRAP_GATE);
    IDTPtr.limit := (SizeOf(TIDTEntry)*256)-1;
    IDTPtr.base := DWord(@IDTTables);
    asm
        lidt [IDTPtr]
    end;
end;
 
procedure init_pic; [public, alias : 'init_pic'];
begin
    outportb(PIC1, ICW1);
    outportb(PIC2, ICW1);
    outportb(PIC1 + 1, pic1);
    outportb(PIC2 + 1, pic2);
    outportb(PIC1 + 1, 4);
    outportb(PIC2 + 1, 2);
    outportb(PIC1 + 1, ICW4);
    outportb(PIC2 + 1, ICW4);
    outportb(PIC1 + 1, 0);
    outportb(PIC2 + 1, 0);
end;
 
end.

Nie będę się tutaj rozpisywał, bo wszystko jest tutaj

int.asm

[BITS 32]
[SECTION .text]
[GLOBAL keys_isr]
keys_isr:
  pusha
  push gs
  push fs
  push es
  push ds
 
  extern keys
  call keys
 
  mov dx, $20 ; \/
  mov al, $20 ; EOI
  out dx, al ;      /\
 
  pop ds
  pop es
  pop fs
  pop gs
  popa
  iret
 
[GLOBAL unknown_interrupt_isr]
unknown_interrupt_isr:
  pusha
  push gs
  push fs
  push es
  push ds
 
  mov dx, $20 ; \/
  mov al, $20 ; EOI
  out dx, al ;      /\
 
  pop ds
  pop es
  pop fs
  pop gs
  popa
  iret
 
[GLOBAL timer_isr]
timer_isr:
  pusha
  push gs
  push fs
  push es
  push ds
 
  extern TimerHandler
  call TimerHandler
 
  mov dx, $20 ; \/
  mov al, $20 ; EOI
  out dx, al ;      /\
 
  pop ds
  pop es
  pop fs
  pop gs
  popa
  iret
 
[GLOBAL floppy_isr]
floppy_isr:
  pusha
  push gs
  push fs
  push es
  push ds
 
  extern FloppyHandler
  call FloppyHandler
 
  mov dx, $20 ; \/
  mov al, $20 ; EOI
  out dx, al ;      /\
 
  pop ds
  pop es
  pop fs
  pop gs
  popa
  iret
 
[SECTION .bss]
[SECTION .data]

Te procedury zostaną wywołane w momencie wystąpienia przerwania i uruchamiają odpowiednie procedury napisane w Pascalu.

pit.pas

unit pit;
 
interface
 
uses console;
 
const
    ClockHz: LongWord = 1193180;
 
var
    TimerTicks: LongWord = 0;
    Ticks: LongWord = 0;
 
procedure init_timer(Hz: LongWord);
procedure Delay(Ticks: LongWord);
procedure TimerHandler;
 
implementation
 
procedure init_timer(Hz: LongWord);
begin
    outportb($43, $36);
    outportb($40, (ClockHz div Hz) and $FF);
    outportb($40, (ClockHz div Hz) >> 8);
end;
 
procedure TimerHandler;[public,alias:'TimerHandler'];
begin
    TimerTicks := TimerTicks + 1;
end;
 
procedure Delay(Ticks: LongWord);
begin
    Ticks := Ticks + TimerTicks;
    While Ticks > TimerTicks do {nic};
end;
 
end.

A to niezwykle proste procedury do zaprogramowania i obsługi timera

floppy.pas

{$ASMMODE INTEL}
 
unit floppy;
 
interface
 
uses pit, console;
 
var
    Waiting : boolean;
    MotorState : byte = 0;
    Size : LongWord;
    Buffer: array[0..5119] of char;
 
const
    DOReg : word = $0002;
    CCReg : word = $0007; // Write-only
    MSReg : word = $0004; // Read Only - Main Status Reg
    FIFOReg : word = $0005; // Write-only
    DriveBase : array[0..1] of word = (
        $03F0, //fda
        $0370); //fdb
 
    FloppyCmd : array[1..19] of byte = (
        $02, // READ_TRACK          1
        $03, // SPECIFY             2
        $04, // SENSE_DRIVE_STATUS  3
        $05, // WRITE_DATA          4
        $06, // READ_DATA           5
        $07, // RECALIBRATE         6
        $08, // SENSE_INTERRUPT     7
        $09, // WRITE_DELETED_DATA  8
        $0A, // READ_ID             9
        $0C, // READ_DELETED_DATA   10
        $0D, // FORMAT_TRACK        11
        $0F, // SEEK                12
        $10, // VERSION             13
        $11, // SCAN_EQUAL          14
        $12, // PERPENDICULAR_MODE  15
        $13, // CONFIGURE           16
        $16, // VERIFY              17
        $19, // SCAN_LOW_OR_EQUAL   18
        $1D);// SCAN_HIGH_OR_EQUAL  19
 
    DriveStatus : array[0..3] of pchar = (
        '0','error','invalid','drive');
 
    MotorOnDelay : integer = {50}0;
    MotorOffDelay : integer = {300}0;
    DMABufLen : word = $4800;
 
procedure FloppyHandler;
function FloppyReset(Drive : byte) : byte;
function FloppyRead(Drive : byte; Data : integer; NoSec : byte) : byte;
function FloppyWrite(Drive : byte; Data : integer; NoSec : byte) : byte;
function FloppyDoTrackLBA(Drive : byte; LBA : integer; dir : byte; NoSectors : byte) : byte;
function FloppyDoTrack(Drive : byte; cyl : byte; dir : byte) : byte;
procedure LBA2CHS(LBA:integer; var cyl,head,sec : byte);
procedure FloppyWriteCmd(Drive : byte; cmd : byte);
function FloppyReadCmd(Drive : byte) : byte;
procedure IRQwait;
procedure SenseInt(Drive : byte; var st0, cyl : byte);
function Calibrate(Drive : byte) : byte;
procedure IODelay;
procedure Motor(Drive : byte; State : byte);
function Seek(Drive : byte; cyli, head : byte):byte;
procedure FloppyDMAInit(dir : byte; Size : LongWord);
 
implementation
 
procedure FloppyHandler;[public,alias:'FloppyHandler'];
begin
     Waiting := false;
end;
 
function FloppyReset(Drive : byte) : byte;
var
    base : word;
    st0, cyl : byte;
begin
    st0 := -1;
    cyl := -1;
 
    Waiting := True;
 
    base := DriveBase[Drive];
 
    outPortb(base + DOReg, $00);
    outPortb(base + DOReg, $0C);
 
    IrqWait;
    SenseInt(Drive, st0, cyl);
 
    outPortb(base + CCReg, $00);
 
    FloppyWriteCmd(Drive, FloppyCmd[2]);
    FloppyWriteCmd(Drive, $DF);
    FloppyWriteCmd(Drive, $02);
 
    if Calibrate(Drive) = 1 then
        FloppyReset := 1
    else
        FloppyReset := 0;
end;
 
procedure IRQwait;
begin
    while Waiting = true do {nic};
end;
 
procedure SenseInt(Drive : byte; var st0, cyl : byte);
var
    base : word;
begin
    base := DriveBase[Drive];
 
    FloppyWriteCmd(Drive, FloppyCmd[7]);
    IODelay;
 
    st0 := FloppyReadCmd(Drive);
    cyl := FloppyReadCmd(Drive);
end;
 
procedure FloppyWriteCmd(Drive : byte; cmd : byte);
var
    base : word;
    i : integer;
begin
    base := DriveBase[Drive];
 
    for i := 1 to 300 do
    begin
        if (($C0 and inPortb(base + MSReg)) = $80) then
        begin
            outPortb((Base + FIFOReg), cmd);
            Exit;
        end;
        Delay(1);
    end;
end;
 
function Calibrate(Drive : byte) : byte;
var
    i, st0, cyl : byte ;
begin
    st0 := -1;
    cyl := -1;
 
    Motor(Drive, 1);
 
    for i:= 0 to 10 do
    begin
        Waiting := True;
        FloppyWriteCmd(Drive, FloppyCmd[6]);
        FloppyWriteCmd(Drive, Drive);
        IrqWait;
        SenseInt(Drive, st0, cyl);
 
        if cyl = 0 then
        begin
            Motor(Drive, 0);
            Calibrate := 0;
            Exit;
        end;
    end;
    Motor(Drive, 0);
    Calibrate := 1;
end;
 
procedure IODelay;
var
    i : integer;
begin
    for i := 1 to 500 do ;
end;
 
function FloppyReadCmd(Drive : byte) : byte;
var
    base : word;
    i : integer;
begin
    base := DriveBase[Drive];
 
    for i := 1 to 300 do
    begin
        if (($80 and inPortb(base + MSReg)) = $80) then
        begin
            FloppyReadCmd := inPortb(base + FIFOReg);
            Exit;
        end;
        Delay(1);
    end;
    FloppyReadCmd := 0;
end;
 
procedure Motor(Drive : byte; State : byte);
var
    base : word;
begin
    base := DriveBase[Drive];
// 0 - Off; 1 - On; 2 - Wait
 
    if State = 1 then
    begin
// If requested on
        if MotorState = 0 then
        begin
    //Turn motor on
            outPortb(base + DOReg, $1C);
            Delay(MotorOnDelay);
            MotorState := 1;
        end
        else
        begin
            Ticks := 300;
            MotorState := 2;
        end;
    end;
 
    if State = 0 then
        if MotorState = 1 then
        begin
            outPortb(base + DOReg, $0C);
            MotorState := 0;
            Delay(MotorOffDelay);
        end;
end;
 
function FloppyRead(Drive : byte; Data : integer; NoSec : byte) : byte;
begin
if Data = 0 then
    FloppyRead := FloppyDoTrack(Drive, Data, 1)
else
    FloppyRead := FloppyDoTrackLBA(Drive, Data, 1, NoSec);
end;
 
function FloppyDoTrackLBA(Drive : byte; LBA : integer; dir : byte; NoSectors : byte) : byte;
var
    cmd, i, st0, st1, st2, rcy, rhe, rse, bps, error : byte;
    cyl, head, sec : byte;
begin
    error := 0;
 
    case dir of
        1  : cmd := FloppyCmd[5] or $C0; //Read
        2  : cmd := FloppyCmd[4] or $C0; //Write
    end;
 
    LBA2CHS(LBA, cyl, head, sec);
    Size := NoSectors * 512;
 
    if (Seek(Drive, cyl, head)) = -1 then
    begin
        FloppyDoTrackLBA := -1;
        Exit;
    end;
 
//  for i := 1 to 20 do
    begin
        Motor(Drive, 1);
        FloppyDMAInit(dir, Size);
 
        Delay(10);
 
        FloppyWriteCmd(Drive, cmd);
        FloppyWriteCmd(Drive, ((head shl 2) or Drive));
        FloppyWriteCmd(Drive, cyl);
        FloppyWriteCmd(Drive, head);
        FloppyWriteCmd(Drive, sec);
        FloppyWriteCmd(Drive, 2);
        FloppyWriteCmd(Drive, 0);
        FloppyWriteCmd(Drive, 0);
        FloppyWriteCmd(Drive, $FF);
 
        IRQWait;
 
        st0 := FloppyReadCmd(Drive);
        st1 := FloppyReadCmd(Drive);
        st2 := FloppyReadCmd(Drive);
 
        //Read Cylinder, Head, Sector
        rcy := FloppyReadCmd(Drive);
        rhe := FloppyReadCmd(Drive);
        rse := FloppyReadCmd(Drive);
 
        bps := FloppyReadCmd(Drive);
 
        if error = 0  then
        begin
            Motor(Drive, 0);
            FloppyDoTrackLBA := 0;
            Exit;
        end;
 
        if error >= 1 then
        begin
            Motor(Drive, 0);
            FloppyDoTrackLBA := -2;
            Exit;
        end;
    end;
 
    Motor(Drive, 0);
    FloppyDoTrackLBA := -1;
end;
 
procedure LBA2CHS(LBA:integer; var cyl,head,sec : byte);
begin
    sec := (LBA mod 18) + 1;
    cyl := LBA Div 36;
    head := (LBA Div 18) mod 2;
end;  
 
function Seek(Drive : byte; cyli, head : byte):byte;
var
    st0, cyl, i : byte;
begin
 
    st0 := -1;
    cyl := -1;
 
    Motor(Drive, 1);
 
    for i := 0 to 10 do
    begin
        Waiting := True;
        FloppyWriteCmd(Drive, FloppyCmd[12]);
        FloppyWriteCmd(Drive, (head shl 2));
        FloppyWriteCmd(Drive, cyli);
        IRQWait;
        SenseInt(Drive, st0, cyl);
 
        if cyl = cyli then
        begin
            Motor(Drive,0);
            Seek := 0;
            Exit;
        end;
    end;
    Seek := -1;
    Motor(Drive, 0);
end;
 
procedure FloppyDMAInit(dir : byte; Size : LongWord);
var
    Addr, Count : Longword;
    mode : byte;
begin
    Addr := DWord(@Buffer);
    Count := Size - 1;
 
    mode := $40;
    mode := mode or $02;
 
    case dir of
        1  : mode := mode or $04; //Read to mem
        2  : mode := mode or $08; //Write from mem
    end;
 
    outPortb($0A,$06); //Mask DMA 2
    outPortb($0C,$FF); //Reset Flip-Flop
 
    outPortb($04,(Addr and $000000FF));         //Address Low byte
    outPortb($04,((Addr shr 8) and $000000FF)); //Address High byte
 
    outPortb($81,((Addr shr 16) and $000000FF)); //External Page Register
 
    outPortb($0C,$FF); //Reset Flip-Flop
    outPortb($05,(Count and $000000FF));         //Count low byte
    outPortb($05,((Count shr 8) and $000000FF)); //Count High byte
 
    outPortb($0B,Mode); //Set Mode -see above
    outPortb($0A,$02);  //Unmask DMA 2
end;
 
function FloppyWrite(Drive : byte; Data : integer; NoSec : byte) : byte;
begin
    FloppyWrite := FloppyDoTrackLBA(Drive, Data, 2, NoSec);
end;
 
function FloppyDoTrack(Drive : byte; cyl : byte; dir : byte) : byte;
var
cmd, i, st0, st1, st2, rcy, rhe, rse, bps, error : byte;
begin
error := 0;
 
case dir of
1  : cmd := FloppyCmd[5] or $C0; //Read
2  : cmd := FloppyCmd[4] or $C0; //Write
end;
 
Size := DMABufLen;
 
if (Seek(Drive, cyl, 0)) or (Seek(Drive, cyl, 1)) = -1 then
  begin
  FloppyDoTrack := -1;
  Exit;
  end;
 
//for i := 1 to 20 do
 begin
   Motor(Drive, 1);
   FloppyDMAInit(dir, Size);
 
   Delay(10);
 
   FloppyWriteCmd(Drive, cmd);
   FloppyWriteCmd(Drive, 0);
   FloppyWriteCmd(Drive, cyl);
   FloppyWriteCmd(Drive, 0);
   FloppyWriteCmd(Drive, 1);
   FloppyWriteCmd(Drive, 2);
   FloppyWriteCmd(Drive, 18);
   FloppyWriteCmd(Drive, $1B);
   FloppyWriteCmd(Drive, $FF);
 
   IRQWait;
 
   st0 := FloppyReadCmd(Drive);
   st1 := FloppyReadCmd(Drive);
   st2 := FloppyReadCmd(Drive);
 
   //Read Cylinder, Head, Sector
   rcy := FloppyReadCmd(Drive);
   rhe := FloppyReadCmd(Drive);
   rse := FloppyReadCmd(Drive);
 
   bps := FloppyReadCmd(Drive);
 
   if error = 0  then
    begin
      Motor(Drive, 0);
      FloppyDoTrack := 0;
      Exit;
    end;
 
   if error >= 1 then
    begin
      Motor(Drive, 0);
      FloppyDoTrack := -2;
      Exit;
    end;
 end;
 
Motor(Drive, 0);
FloppyDoTrack := -1;
end;
 
end.

Tutaj już trochę bardziej zaawansowana część systemu odczytująca i zapisująca pojedyncze sektory z/na dyskietkę. Po wywołaniu odpowiedniej procedury w zmiennej Buffer znajdzie się wybrany sektor. Chcąc zapisać coś na dyskietkę należy wywołać:

Buffer[0]:='t';
Buffer[1]:='e';
Buffer[2]:='s';
Buffer[3]:='t';
FloppyWrite(0, 0, 1);

To zapisze w bootsectorze test.

fat.pas

{$ASMMODE INTEL}
 
unit fat;
 
interface
type
    TCurrentDir = packed record 
        Name: array [0..10] of char;
        Attribute: char;
        NTRes: char;
        A: char;
        CrtTime: word;
        CrtDate: word;
        LastAccessDate: word;
        FirstClusterHi: word;
        LastWriteTime: word;
        LastWriteDate: word;
        FirstClusterLo: word;
        FileSize: dword;
    end;
 
var
    BootSector: packed record
        SysName: array [0..7] of char;
        BytesPerSector: word;
        SectorsPerCluser: byte;
        ReservedSectors: word;
        NumberFATs: byte;
        MaxRootEntries: word;
        TotalSectorNumber: word;
        Media: byte;
        FATSectorNumber: word;
        SectorsPerTrack: word;
        HeadNumber: word;
        HiddenSectors: dword;
        TotalSectorNumber32: dword;
        DriveNumber: byte;
        Reserved1: char;
        BootSignature: char;
        VolumeSerial: dword;
        VolumeLabel: array [0..10] of char;
        FileSystemType: array [0..7] of char;
    end;
 
    FATCell: array [0..4095] of word;
    CurrentDir: array [0..223] of TCurrentDir;
    PathCurrentDir: pchar ='/';
    RootDir: array [0..65535] of char;
 
procedure LoadBootSector;
procedure LoadDir(path: pchar);
function ClusterToSector(cluster: integer): integer;
procedure ReadFile(Name: pchar);
function FindFile(Name: pchar): integer;
procedure LoadFAT;
procedure List(dir:pchar);
 
implementation
 
uses floppy, console;
 
procedure LoadFAT;
var
i:integer=0;
ii:integer=0;
begin
    LoadBootSector;
    for i:=0 to 511 do
        Buffer[i]:=#0;
 
    FloppyRead(0, BootSector.ReservedSectors, BootSector.FATSectorNumber);
    for i:=0 to 1536 do
    begin
        FATCell[i*2]:= ord(byte(Buffer[ii+1]){>>4} and $0F) << 8 or ord(Buffer[ii]);
        FATCell[i*2+1]:= ord(Buffer[ii+2]) << 4 or ord(byte(Buffer[ii+1])>>4 and $0F);
        ii:=ii+3;
    end;
end;
 
procedure LoadBootSector;
var
i: integer = 0;
begin
    for i:=0 to 511 do
        Buffer[i]:=#0;
    FloppyRead(0, 0, 1);
    BootSector.SysName[0] := Buffer[3];
    BootSector.SysName[1] := Buffer[4];
    BootSector.SysName[2] := Buffer[5];
    BootSector.SysName[3] := Buffer[6];
    BootSector.SysName[4] := Buffer[7];
    BootSector.SysName[5] := Buffer[8];
    BootSector.SysName[6] := Buffer[9];
    BootSector.SysName[7] := Buffer[10];
    BootSector.BytesPerSector := ord(Buffer[12]) << 8 or ord(Buffer[11]);
    BootSector.SectorsPerCluser := ord(Buffer[13]);
    BootSector.ReservedSectors := ord(Buffer[15]) << 8 or ord(Buffer[14]);
    BootSector.NumberFATs := ord(Buffer[16]);
    BootSector.MaxRootEntries := ord(Buffer[18]) << 8 or ord(Buffer[17]);
    BootSector.TotalSectorNumber := ord(Buffer[20]) << 8 or ord(Buffer[19]);
    BootSector.Media := ord(Buffer[21]);
    BootSector.FATSectorNumber := ord(Buffer[23]) << 8 or ord(Buffer[22]);
    BootSector.SectorsPerTrack := ord(Buffer[25]) << 8 or ord(Buffer[24]);
    BootSector.HeadNumber := ord(Buffer[27]) << 8 or ord(Buffer[26]);
    BootSector.HiddenSectors := ord(Buffer[31]) << 24 or ord(Buffer[30]) << 16 or ord(Buffer[29]) << 8 or ord(Buffer[28]);
    BootSector.TotalSectorNumber32 := ord(Buffer[35]) << 24 or ord(Buffer[34]) << 16 or ord(Buffer[33]) << 8 or ord(Buffer[32]);
    BootSector.DriveNumber := ord(Buffer[36]);
    BootSector.Reserved1 := Buffer[37];
    BootSector.BootSignature := Buffer[38];
    BootSector.VolumeSerial := ord(Buffer[42]) << 24 or ord(Buffer[41]) << 16 or ord(Buffer[40]) << 8 or ord(Buffer[39]);
    BootSector.VolumeLabel[0] := Buffer[43];
    BootSector.VolumeLabel[1] := Buffer[44];
    BootSector.VolumeLabel[2] := Buffer[45];
    BootSector.VolumeLabel[3] := Buffer[46];
    BootSector.VolumeLabel[4] := Buffer[47];
    BootSector.VolumeLabel[5] := Buffer[48];
    BootSector.VolumeLabel[6] := Buffer[49];
    BootSector.VolumeLabel[7] := Buffer[50];
    BootSector.VolumeLabel[8] := Buffer[51];
    BootSector.VolumeLabel[9] := Buffer[52];
    BootSector.VolumeLabel[10] := Buffer[53];
    BootSector.FileSystemType[0] := Buffer[54];
    BootSector.FileSystemType[1] := Buffer[55];
    BootSector.FileSystemType[2] := Buffer[56];
    BootSector.FileSystemType[3] := Buffer[57];
    BootSector.FileSystemType[4] := Buffer[58];
    BootSector.FileSystemType[5] := Buffer[59];
    BootSector.FileSystemType[6] := Buffer[60];
    BootSector.FileSystemType[7] := Buffer[61];
 
end;
 
procedure LoadDir(path: pchar);
var 
    i: integer =0;
    a: integer =0;
begin
    if (path[0] = '/') and (path[1] = #0) then
    begin
        for a:=0 to 511 do
            Buffer[a]:=#0;
        for i:=0 to 13 do
        begin
            FloppyRead(0, (BootSector.ReservedSectors + (BootSector.FATSectorNumber * BootSector.NumberFATs)) + i, 1);
            for a:=0 to 15 do
            begin
                CurrentDir[a+i*16].Name[0] := Buffer[a*32+0];
                CurrentDir[a+i*16].Name[1] := Buffer[a*32+1];
                CurrentDir[a+i*16].Name[2] := Buffer[a*32+2];
                CurrentDir[a+i*16].Name[3] := Buffer[a*32+3];
                CurrentDir[a+i*16].Name[4] := Buffer[a*32+4];
                CurrentDir[a+i*16].Name[5] := Buffer[a*32+5];
                CurrentDir[a+i*16].Name[6] := Buffer[a*32+6];
                CurrentDir[a+i*16].Name[7] := Buffer[a*32+7];
                CurrentDir[a+i*16].Name[8] := Buffer[a*32+8];
                CurrentDir[a+i*16].Name[9] := Buffer[a*32+9];
                CurrentDir[a+i*16].Name[10] := Buffer[a*32+10];
 
                CurrentDir[a+i*16].Attribute := Buffer[a*32+11];
                CurrentDir[a+i*16].NTRes := Buffer[a*32+12];
                CurrentDir[a+i*16].A := Buffer[a*32+13];
                CurrentDir[a+i*16].CrtTime := ord(Buffer[a*32+15]) << 8 or ord(Buffer[a*32+14]);
                CurrentDir[a+i*16].CrtDate := ord(Buffer[a*32+17]) << 8 or ord(Buffer[a*32+16]);
                CurrentDir[a+i*16].LastAccessDate := ord(Buffer[a*32+19]) << 8 or ord(Buffer[a*32+18]);
                CurrentDir[a+i*16].FirstClusterHi := ord(Buffer[a*32+21]) << 8 or ord(Buffer[a*32+20]);
                CurrentDir[a+i*16].LastWriteTime := ord(Buffer[a*32+23]) << 8 or ord(Buffer[a*32+22]);
                CurrentDir[a+i*16].LastWriteDate := ord(Buffer[a*32+25]) << 8 or ord(Buffer[a*32+24]);
                CurrentDir[a+i*16].FirstClusterLo := ord(Buffer[a*32+27]) << 8 or ord(Buffer[a*32+26]);
                CurrentDir[a+i*16].FileSize := ord(Buffer[a*32+29]) << 24 or ord(Buffer[a*32+28]) << 16 or ord(Buffer[a*32+27]) << 8 or ord(Buffer[a*32+29]);
            end;
        end;
    end;
end;
 
function ClusterToSector(cluster: integer): integer;
begin
    ClusterToSector:= (BootSector.ReservedSectors + (BootSector.FATSectorNumber * BootSector.NumberFATs)) + ((BootSector.MaxRootEntries * 32) div 512) - 2 + cluster;
end;
 
function FindFile(Name: pchar): integer;
var
    i: integer;
begin
    for i:=0 to 223 do
    begin
        if (CurrentDir[i].Name[0] = Name[0]) and 
        (CurrentDir[i].Name[1] = Name[1]) and
        (CurrentDir[i].Name[2] = Name[2]) and
        (CurrentDir[i].Name[3] = Name[3]) and
        (CurrentDir[i].Name[4] = Name[4]) and
        (CurrentDir[i].Name[5] = Name[5]) and
        (CurrentDir[i].Name[6] = Name[6]) and
        (CurrentDir[i].Name[7] = Name[7]) and
        (CurrentDir[i].Name[8] = Name[8]) and
        (CurrentDir[i].Name[9] = Name[9]) and
        (CurrentDir[i].Name[10] = Name[10]) then
        begin
            FindFile := i;
            Exit;
        end;
    end;
    PrintStr('Nie odnaleziono pliku');
end;
 
procedure ReadFile(Name: pchar);
var
a :integer;
CurrentCluster:integer;
begin
    LoadBootSector;
    LoadFAT;
    LoadDir('/');
    for a:=0 to 511 do
        Buffer[a]:=#0;
    FloppyRead(0, ClusterToSector(CurrentDir[FindFile(Name)].FirstClusterLo) , 1);
    CurrentCluster:=CurrentDir[FindFile(Name)].FirstClusterLo;
    PrintStr(#13);
    for a:=0 to 511 do
        PrintChar(Buffer[a]);
 
    while (FATCell[CurrentCluster] <> $FFF) do
    begin
        for a:=0 to 511 do
            Buffer[a]:=#0;
        FloppyRead(0, ClusterToSector(FATCell[CurrentCluster]) , 1);
        CurrentCluster:=FATCell[CurrentCluster];
        for a:=0 to 511 do
            PrintChar(Buffer[a]);
    end;
end;
 
procedure List(dir:pchar);
var
    b:integer=0;
begin
    LoadBootSector;
    LoadDir(dir);
    repeat
        if (CurrentDir[b].Name[0] <> char($E5)) and (CurrentDir[b].Attribute <> char($0F)) then
        begin
            PrintStr(#13);
            PrintChar(CurrentDir[b].Name[0]);
            PrintChar(CurrentDir[b].Name[1]);
            PrintChar(CurrentDir[b].Name[2]);
            PrintChar(CurrentDir[b].Name[3]);
            PrintChar(CurrentDir[b].Name[4]);
            PrintChar(CurrentDir[b].Name[5]);
            PrintChar(CurrentDir[b].Name[6]);
            PrintChar(CurrentDir[b].Name[7]);
            PrintChar(CurrentDir[b].Name[8]);
            PrintChar(CurrentDir[b].Name[9]);
            PrintChar(CurrentDir[b].Name[10]);
        end;
        b:=b+1;
    until CurrentDir[b].Name[0] = char($00);
end;
 
end.

console.pas

unit Console;
 
interface
 
const
    Black = 0;
    Blue = 1;
    Green = 2;
    Cyan = 3;
    Red = 4;
    Magenta = 5;
    Brown = 6;
    LightGray = 7;
    DarkGray = 8;
    LightBlue = 9;
    LightGreen = 10;
    LightCyan = 11;
    LightRed = 12;
    LightMagenta = 13;
    Yellow = 14;
    White = 15;
 
var
    Screen: pchar =  PChar ($B8000);
    q, CursorX, CursorY: integer;
    Color: char;
    Background: integer = 0;
    backspace: array [0..4000] of integer;
    Command: PChar;
    e: integer = 0;
 
procedure ClearScreen;
procedure PrintStr(text: PChar);
procedure PrintInt(number:integer);
procedure PrintChar(ch: Char);
procedure SetColor(txt,back:integer);
procedure SetXY(x,y: integer);
procedure Scroll;
procedure Command2;
 
implementation
 
uses keyboard, pit, floppy, fat;
 
procedure ClearScreen; [public, alias: 'ClearScreen'];
var
    i: integer;
begin
    for i:=0 to 2000 do
    begin
        Screen[i*2]:=#0;
        Screen[i*2-1]:=char(white);
    end;
end;
 
procedure PrintStr(text: PChar);[public, alias: 'PrintStr'];
var
    address: Word;
    i: integer;
begin
    i:=0;
    if (CursorX > 79) or (text[i] = #13) then 
    begin
        SetXY(0, CursorY+1);
    end 
    else
    begin
        if (text[i] = #10) then
        begin
            SetXY(CursorX, CursorY+1);
        end
    else
    begin
        if (CursorY > 24) then
            begin
                SetXY(CursorX, 24);
                Scroll;
            end;
 
    repeat
        address:= CursorX*2 + CursorY * 160;
        Screen[address]:= text[i];
        Screen[address+1]:= color;  
        backspace[address]:=0;
        backspace[address+1]:=0;
        SetXY(CursorX+1, CursorY);
        i:=i + 1;
    until text[i] = #0
 
        end;
    end;
end;
 
procedure PrintInt(number: integer); [public, alias: 'PrintInt'];
var
    chars: array [0..11] of Char;
    negative: Boolean;
    i: integer;
    txt: PChar;
begin
    txt:= @chars[12];
    i:=11;
    negative:= False;
    if number<0 then 
    begin 
        number:=-number;
        negative:= True;
    end;
 
    repeat
        chars[i]:= char((number mod 10) + byte('0'));
        number:= number div 10;
        number-=1;
        Dec(txt);
    until number=0;
 
    if negative=True then
    begin
        PrintStr('-');
    end;
 
    PrintStr(txt);
end;
 
procedure PrintChar(ch: Char);[public, alias: 'PrintChar'];
var
    address: Word;
begin
    if (CursorX > 79) or (ch = #13) then
        SetXY(0, CursorY+1)
    else
    if ch = #10 then
        SetXY(CursorX, CursorY+1)
    else
    if CursorY > 24 then
    begin
        SetXY(CursorX, 24);
        Scroll;
    end;
 
    address:= CursorX*2 + CursorY * 160;
    Screen[address]:= ch;
    Screen[address+1]:= color;  
    backspace[address]:=0;
    backspace[address+1]:=0;
    SetXY(CursorX+1, CursorY);
end;
 
procedure SetColor(txt,back: integer); [public, alias: 'SetColor'];
begin
    color:=char(txt+back*16);
end;
 
procedure Scroll;[public, alias: 'Scroll'];
var
    address : word;
begin
    for address:=0 to 1920 do
    begin
        Screen[address*2] := Screen[address*2+160];
        Screen[address*2+1] := Screen[address*2+1+160];
    end;
 
    for address:=1921 to 2000 do
    begin
        Screen[address*2]:=#0;
        Screen[address*2+1]:=char(15); 
    end;        
end;
 
procedure SetXY(x,y: integer);[public, alias: 'SetXY'];
var
    temp: integer;
begin
    CursorX := x;
    CursorY := y;
    temp:=y*80+x;
    outportb($3D4, 14);
    outportb($3D5, temp>>8);
    outportb($3D4, 15);
    outportb($3D5, temp);
end;
 
procedure Command2;[public,alias:'Command2'];
var 
    pol : integer= 0;
    drive : byte;
    a:integer;
    i : integer = 0;
    Root_Dir : pchar = '/';
    q : pchar;
    b:integer=0;
    wynik: pchar;
    ii:integer =0;
begin
    cmd := 0;
    if (Command[0]='L') and (Command[1]='I') and (Command[2]='S') and (Command[3]='T') and (Command[4]=#0) then
    begin // list
    List('/');
    end
    else
    if (Command[0]='R') and (Command[1]='E') and (Command[2]='A') and (Command[3]='D') and (Command[4]=' ') then
    begin // read
        repeat
        begin
            wynik[i]:=command[i+5];
            i:=i+1;
        end;
                until command[i+4] = #0;
        ReadFile(wynik);
    end
    else
    if (Command[0]='E') and (Command[1]='X') and (Command[2]='I') and (Command[3]='T') and (Command[4]=#0) then
    begin // exit
        asm
            mov al, 0feh
            out 64h, al
        end;
    end
    else
    if (Command[0]='H') and (Command[1]='E') and (Command[2]='L') and (Command[3]='P') and (Command[4]=#0) then
    begin // help
        PrintStr(#13);
        PrintStr('EXIT               - resetuje komputer');
        PrintStr(#13);
        PrintStr('HELP               - wyswietla pomoc');
        PrintStr(#13);
        PrintStr('READ <nazwa pliku> - odczytuje wskazany plik z dyskietki');
        PrintStr(#13);
        PrintStr('LIST               - wyswietla liste plikow z glownego katalogu');
    end
    else
    begin
        PrintStr(#13);
        PrintStr('Polecenie "');
        PrintStr(Command);
        PrintStr('" nie zostalo odnalezione.');
    end;
 
    e:=0;
    PrintStr(#13);
    SetColor(LightGray,Black);
    PrintStr(PathCurrentDir);
    PrintStr('>');
    SetColor(White,Black);
    repeat //czyszczenie zmiennej "Command"
        Command[pol]:=#0;
        pol+=1;
    until pol=100;
end;
 
end.

keyboard.pas

unit Keyboard;
 
interface
uses console, fat;
 
procedure Shell;
procedure keys;
procedure PrintKey(ch: Char);
 
var
    address: Word;
    cmd: integer =0;
 
implementation
const
    F1=59;
    F2=60;
    F3=61;
    F4=62;
    F5=63;
    F6=64;
    F7=65;
    F8=66;
    F9=67;
    F10=68;
    F11=87;
    F12=88;
    Enter=28;
    Crtl=29;
    Alt=56;
    BkSpc=14;
    Space=57;
    Shift=42;
    CapsLock=58;
    up=72 ;
    left=75;
    right=77;
    down=80 ;
 
    tblchar : array [1..57] of char=
       ('0','1','2','3','4','5','6','7','8','9','0','?','=','0',' ',
       'Q','W','E','R','T','Y','U','I','O','P','[',']','0','0',
       'A','S','D','F','G','H','J','K','L',' ','{','}','0','0',
       'Z','X','C','V','B','N','M',',','.','/','0','*','0',' ');
 
procedure Shell;[public, alias: 'Shell'];
var
i: integer;
begin
    ClearScreen;
    SetColor(Green,Black);
    PrintStr('4pOS 0.3');
    PrintStr(#13);
    SetColor(LightGray,Black);
    PrintStr(PathCurrentDir);
    PrintStr('>');
    SetColor(White,Black);
    for i:=0 to 100 do //czyszczenie zmiennej "Command"
        Command[i]:=#0;
end;
 
procedure keys;[public, alias: 'keys'];
var
    b: Byte;
begin
    b:= inportb($60);
    if b>$7F then
    else
    begin
//BACKSPACE
    if b = BkSpc then
    begin 
        address:=CursorX*2+CursorY*160;
        if backspace[address-2] = 1 then
        begin
            CursorX := CursorX - 1;
            Screen[CursorX*2+CursorY*160]:=#0;
            cmd:=cmd-1;
            Command[cmd]:=#0;
            SetXY(CursorX,CursorY);
        end;
    end;
//ENTER
    if b = enter then
    begin 
        Command2; 
    end
    else
    begin
        if (b <> enter) and (b <> BkSpc) then
        begin
            PrintKey(tblchar[b]);
            Command[cmd]:=tblchar[b];
            cmd:=cmd+1;
        end;
    end;
end;
end;
 
procedure PrintKey(ch: Char);
var
    address: Word;
begin
    if (CursorX > 79) or (ch = #13) then
        SetXY(0, CursorY+1)
    else
    if ch = #10 then
        SetXY(CursorX, CursorY+1)
    else
    if CursorY > 24 then
    begin
        SetXY(CursorX, 24);
        Scroll;
    end;
 
    address:= CursorX*2 + CursorY * 160;
    Screen[address]:= ch;
    Screen[address+1]:= color;  
    backspace[address]:=1;
    backspace[address+1]:=1;
    SetXY(CursorX+1, CursorY);
end;
 
end.

system.pas

unit system;
 
interface
 
function inportb(port:word):byte;
procedure outportb(port, zn:word);
 
type
 cardinal = 0..$FFFFFFFF;
 hresult = cardinal;
 dword = cardinal;
 integer = longint;
 
 pchar = ^char;
 
implementation
 
function inportb(port:word):byte;[public, alias: 'inportb'];
var
    temp : byte ;
begin
    asm
        mov dx,port
        in al,dx
        mov temp , al
    end;
end;
 
procedure outportb(port, zn:word);[public, alias: 'outportb'];
var
    zz:char;
begin
    zz:=char(zn);
    asm
        mov dx, port
        mov al, zz
        out dx, al
    end;
end;
 
end.

kernel.ld

OUTPUT_FORMAT("elf32-i386")
ENTRY(_start)
SECTIONS
{
  .text  0x100000 :
  {
    text = .; _text = .; __text = .;
    *(.text)
    . = ALIGN(4096);
  }
  .data  :
  {
    data = .; _data = .; __data = .;
    *(.data)
    kimage_text = .;
    LONG(text);
    kimage_data = .;
    LONG(data);
    kimage_bss = .;
    LONG(bss);
    kimage_end = .;
    LONG(end);
    . = ALIGN(4096);
  }
  .bss  :
  {
    bss = .; _bss = .; __bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .; _end = .; __end = .;
}

Makefile

4pOS: start int kernel
    ld --emit-relocs output/start.o output/kernel.o output/system.o output/keyboard.o output/console.o output/idt.o output/int.o output/pit.o output/floppy.o output/fat.o -T"src/kernel.ld" -o "4pOS.bin"

start: 
    nasm -f elf src/kernel/start.asm -o output/start.o

int: 
    nasm -f elf src/kernel/int.asm -o output/int.o

kernel: 
    fpc -a -Anasmelf src/kernel/kernel.pas -Fu"src/shell" -Fu"src/kernel" -FE"output" -O3 -Op3 -Si -Sc -Sg -Xd -Tlinux -Rintel

install: 4pOS
    sudo mount 4pos.ima img -o loop
    sudo cp 4pOS.bin img
    sudo umount img

clean:
    rm output/*

run: install
    qemu -fda 4pos.ima

Zakończenie

Standardowo zrzut ekranu
4pOS-0.3.png
i źródła 4pOS-0.3.zip

18 komentarzy

Po wielu godzinach gapienia się w kod, znalazłem linijkę, przez którą po wpisaniu list/read migała spacja: while Waiting = true do {nic};
Mój komentarz do tej linijki: JA PIERD*OLE! JAK TAKIE COŚ MOŻE POWODOWAĆ TYLE BŁĘDÓW??!!
:)

Mogłeś umieścić jakiś opis, co kod wykonuje w danej chwili, na czym to wszystko polega, a nie jedynie podać źródło, choć do niego pretensji większych nie mam, ale system rzeczywiście czasami ma problemy z obsługą systemu plików (LIST, READ) :(.

Po napisaniu czwartej części (ostatniej) planuję kurs przepisać żeby było tak ja należy

fajny system (troche niedopracowany, jeżeli chodzi o LIST i READ), ale ogólnie spoko

lukasz1235 !!! wymiatasz... ja kiedys probowalem napisac os'a w c czy c++ ale na pascalu i delphi znam sie troche lepiej...

Zawiesza się(ten system w QEMU). Ale jak nie skompiluje sam(su, make, make install), tylko spróbuję uruchomić tą wersję skompilowaną przez ciebie to działa normalnie.

Dziwne. U mnie działa. A co dokładnie się dzieje?

Pod QEMU nie działa ani komenda LIST, ani READ.

funkcja uppercase by się przydała
aha i w ms virtual pc jak dam READ <nieistniejący plik> to pisze mi w nieskończoność spacje albo nierozdzielaka (alt + 0160)

Z lenistwa. Jako parametr read trzeba było podać duże litery.

a czemu przestawiłeś się na duże litery?

To pewnie wina niedopracowanego sterownika do floppa

troche na "odpieprz", bardziej pasowało?by do gotowców.
A przy komendach LIST i READ wywala VMWare

możesz podać jakiś namiar do siebie, lub napisz na mój mail ;]
też się interesuję pisaniem OS'a , a Twój jest najbardziej rozbudowanym ze wszystkich jakie do tej pory znalazłem w necie (chodzi o samoróbki) :))

Nie mam pojęcia jak to skompilować pod Windowsem. Nigdy nie próbowałem

Spacje migają tylko jeżeli wpiszesz jako parametr nieistniejący plik

lukasz1235 kiedy będzie kolejna część, niestety komenda READ nie działa mi (na Virtual Box), tak jak kolegom niżej, tylko spacje migają.
Możesz podać jakiś kontakt do siebie, mam kilka pytań odnośnie kompilacji (lub napisz [email protected]). Jakiej wersji Free Pascala używać pod winde bo mi nie kompiluje plików. ;/

[email protected]

Widziałem bardziej zaawansowane systemy