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ęp2 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 _startGLOBAL _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)
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
Ciekawe
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