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 idt.pas
4 pit.pas
5 fat.pas
6 keyboard.pas
7 kernel.ld
8 Makefile

start.asm

```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]


<h1>kernel.pas</h1>
```delphi
{$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

```delphi {$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 <a href="http://4programmers.net/Os_programming/Pisanie_systemów_operacyjnych_cz._III_-_przerwania,_wyjątki,_GRUB.">tutaj</a>

<h1>int.asm</h1>
```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

```delphi 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

<h1>floppy.pas</h1>
```delphi
{$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

```delphi {$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+i16].Name[0] := Buffer[a32+0];
CurrentDir[a+i16].Name[1] := Buffer[a32+1];
CurrentDir[a+i16].Name[2] := Buffer[a32+2];
CurrentDir[a+i16].Name[3] := Buffer[a32+3];
CurrentDir[a+i16].Name[4] := Buffer[a32+4];
CurrentDir[a+i16].Name[5] := Buffer[a32+5];
CurrentDir[a+i16].Name[6] := Buffer[a32+6];
CurrentDir[a+i16].Name[7] := Buffer[a32+7];
CurrentDir[a+i16].Name[8] := Buffer[a32+8];
CurrentDir[a+i16].Name[9] := Buffer[a32+9];
CurrentDir[a+i16].Name[10] := Buffer[a32+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.


<h1>console.pas</h1>
```delphi
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

```delphi 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:=CursorX2+CursorY160;
if backspace[address-2] = 1 then
begin
CursorX := CursorX - 1;
Screen[CursorX2+CursorY160]:=#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.


<h1>system.pas</h1>
```delphi
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


<h1>Zakończenie</h1>
Standardowo zrzut ekranu
![4pOS-0.3.png](//static.4programmers.net/uploads/attachment/4ccd36dc6b330.png)
i źródła [4pOS-0.3.zip](//4programmers.net/Download/4818/38)

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