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