در مقاله قبل دیدیم که پردازنده 8086 بعد از Reset اجرای کد را از آدرس فیزیکی نزدیک انتهای اولین مگابایت حافظه، یعنی 0xFFFF0 شروع می‌کند.

همچنین دیدیم که این آدرس در Real Mode می‌تواند به شکل FFFF:0000 نمایش داده شود.

اما اینجا چند سؤال مهم ایجاد می‌شود:

چرا آدرس از دو قسمت تشکیل شده است؟

عدد معروف 640KB از کجا آمده است؟

اگر BIOS در بالای حافظه قرار دارد، پایین حافظه چه چیزهایی وجود دارند؟

وقفه‌هایی مثل INT 10h یا INT 13h که در برنامه‌های Assembly قدیمی می‌بینیم چه ارتباطی با این حافظه دارند؟

برای پاسخ به این سؤال‌ها باید نقشه حافظه PCهای کلاسیک را کمی دقیق‌تر ببینیم.

چرا فقط یک مگابایت؟

پردازنده‌های 8086 و 8088 دارای Address Bus بیست‌بیتی هستند.

با 20 بیت می‌توان:

2^20

آدرس مختلف ایجاد کرد. یعنی:

1,048,576 bytes
=
1 MB

پس فضای آدرس فیزیکی این پردازنده‌ها از:

0x00000

شروع می‌شود و تا:

0xFFFFF

ادامه پیدا می‌کند.

0x00000
   |
   |
   |       1 MB Address Space
   |
   |
0xFFFFF

اما یک مشکل وجود دارد. رجیسترهای اصلی 8086 فقط 16 بیتی هستند. یک مقدار 16 بیتی فقط می‌تواند 65536 حالت مختلف داشته باشد؛ یعنی اگر قرار بود یک Register به‌تنهایی آدرس حافظه را نگه دارد، فقط 64KB قابل آدرس‌دهی بود.

پس CPU چطور می‌تواند با Registerهای 16 بیتی به یک فضای یک مگابایتی دسترسی داشته باشد؟ جواب همان مدل معروف Segment:Offset است.

Segment و Offset چگونه کار می‌کنند؟

در Real Mode یک آدرس معمولاً از دو مقدار 16 بیتی تشکیل می‌شود:

Segment:Offset

برای تبدیل این دو مقدار به آدرس فیزیکی، Segment چهار بیت به سمت چپ Shift می‌شود. به بیان ساده‌تر:

Segment × 16

و سپس Offset به آن اضافه می‌شود:

Physical Address =
Segment × 16 + Offset

مثلاً برای:

1234:5678

ابتدا داریم:

1234h × 10h = 12340h

سپس:

12340h + 5678h = 179B8h

پس آدرس فیزیکی می‌شود:

0x179B8

این روش به پردازنده اجازه می‌دهد با دو مقدار 16 بیتی، فضایی بسیار بزرگ‌تر از 64KB را آدرس‌دهی کند.

Segment واقعاً چیست؟

یک اشتباه رایج این است که Segment را مثل یک بلوک کاملاً جدا و ثابت 64KB در حافظه تصور کنیم. اما Real Mode این‌طور کار نمی‌کند.

مقدار Segment در واقع برای ساختن یک Base Address استفاده می‌شود.

مثلاً:

1000:0000

به آدرس:

0x10000

اشاره می‌کند. اما:

1001:0000

فقط 16 بایت جلوتر است:

0x10010

یعنی شروع یک Segment جدید می‌تواند هر 16 بایت اتفاق بیفتد. به همین دلیل Segmentها می‌توانند به‌شدت با یکدیگر هم‌پوشانی داشته باشند.

چند آدرس برای یک مکان حافظه

یکی از نتایج جالب این روش این است که یک آدرس فیزیکی می‌تواند با چند Segment:Offset مختلف نمایش داده شود.

مثلاً:

1000:0010

می‌شود:

10000h + 0010h
=
10010h

اما:

1001:0000

هم برابر است با:

10010h + 0000h
=
10010h

بنابراین 1000:0010 و 1001:0000 هر دو به همان Byte در حافظه اشاره می‌کنند. این همان مفهومی است که در مقاله قبل هنگام مقایسه FFFF:0000 و F000:FFF0 دیدیم.

این یک مگابایت چه چیزهایی داخل خودش دارد؟

تمام فضای یک مگابایت به RAM آزاد برنامه‌ها اختصاص داده نشده است.

در PCهای کلاسیک، نقشه کلی حافظه تقریباً چنین شکلی دارد:

0x00000
+--------------------------------+
|                                |
|      Conventional Memory       |
|                                |
|             640 KB             |
|                                |
+--------------------------------+
0xA0000

|                                |
| Video Memory                   |
| Hardware Areas                 |
| Option ROMs                    |
|                                |

0xF0000
+--------------------------------+
|          System BIOS           |
+--------------------------------+
0xFFFFF

اولین 640KB، یعنی محدوده تقریبی 0x00000 - 0x9FFFF، برای RAM معمول سیستم در نظر گرفته شد. این بخش را معمولاً Conventional Memory یا Base Memory می‌نامیم.

پس 384KB باقی‌مانده چه شد؟

از 0xA0000 تا 0xFFFFF، 384KB دیگر باقی می‌ماند. در طراحی PCهای کلاسیک این ناحیه برای RAM معمول برنامه‌ها در نظر گرفته نشده بود.

بخش‌هایی از آن برای Video Memory، Video BIOS، Option ROMهای کارت‌های توسعه، نواحی سخت‌افزاری و System BIOS استفاده می‌شد.

این قسمت معمولاً با عنوان Upper Memory Area یا UMA شناخته می‌شود.

00000
+-----------------------------+
|                             |
|    Conventional Memory      |
|                             |
|          640 KB             |
|                             |
+-----------------------------+
A0000

+-----------------------------+
|                             |
|        Video Memory         |
|                             |
+-----------------------------+
C0000

+-----------------------------+
| Video BIOS / Option ROMs    |
| Expansion Hardware ROMs     |
+-----------------------------+

           ...

F0000
+-----------------------------+
|                             |
|         System BIOS         |
|                             |
+-----------------------------+
FFFFF

مرزبندی دقیق این بخش‌ها در تمام سیستم‌ها الزاماً یکسان نیست، اما این نقشه تصویر خوبی از معماری عمومی PCهای کلاسیک ارائه می‌دهد.

ابتدای Conventional Memory هم کاملاً آزاد نیست

ممکن است در نگاه اول فکر کنیم از آدرس صفر تا 640KB همه‌چیز RAM آزاد برای DOS و برنامه‌هاست. اما این‌طور نیست.

بخش‌های ابتدایی این حافظه برای ساختارهای مهم سیستم استفاده می‌شوند. یکی از مهم‌ترین آنها Interrupt Vector Table است.

Interrupt Vector Table چیست؟

معماری 8086 برای 256 شماره Interrupt فضا در نظر گرفته است. هر Interrupt باید راهی داشته باشد تا CPU بفهمد Handler مربوط به آن در کجای حافظه قرار گرفته است.

برای همین جدولی در ابتدای حافظه وجود دارد که آدرس Handlerها را نگه می‌دارد. نام آن Interrupt Vector Table یا IVT است.

IVT از آدرس فیزیکی:

0x00000

شروع می‌شود. هر Vector چهار بایت فضا دارد:

2 bytes -> Offset
2 bytes -> Segment

و چون 256 Vector داریم:

256 × 4 = 1024 bytes

پس IVT دقیقاً اولین یک کیلوبایت فضای حافظه را اشغال می‌کند:

0x00000
      |
      | Interrupt Vector Table
      |       1024 bytes
      |
0x003FF

وقتی می‌نویسیم INT 10h چه اتفاقی می‌افتد؟

فرض کنیم یک برنامه این دستور را اجرا کند:

INT 10h

عدد 10h شماره Interrupt است. برای پیدا کردن محل Vector مربوط به آن، شماره Interrupt در چهار ضرب می‌شود:

10h × 4 = 40h

بنابراین Vector مربوط به Interrupt شماره 10h از محل:

0x00040

در IVT شروع می‌شود.

آنجا آدرس Segment:Offset مربوط به Handler ذخیره شده است. CPU آن آدرس را می‌خواند و کنترل اجرای برنامه را به Handler منتقل می‌کند.

Program
   |
   | INT 10h
   v
Interrupt Vector Table
   |
   | find vector 10h
   v
Address of Handler
   |
   v
Interrupt Handler

اینجا است که سه مفهومی که تا اینجا درباره آنها صحبت کردیم به یکدیگر متصل می‌شوند: Memory، Segment:Offset و Interrupts.

BIOS Interruptها وارد داستان می‌شوند

BIOS فقط برای لحظه روشن شدن کامپیوتر و اجرای POST نیست. در PCهای کلاسیک، Firmware مجموعه‌ای از Routineها را هم در اختیار نرم‌افزار قرار می‌دهد.

نرم‌افزار می‌تواند از طریق Interruptهای مشخص این Routineها را فراخوانی کند.

برای مثال INT 10h معمولاً به سرویس‌های Video BIOS مربوط است. نرم‌افزار از این طریق می‌تواند عملیاتی مثل تغییر Video Mode، جابه‌جایی Cursor یا نمایش Character را درخواست کند.

Interrupt مهم دیگری INT 13h است که با سرویس‌های Disk BIOS ارتباط دارد. از طریق آن می‌توان عملیاتی مانند خواندن Sector یا دسترسی به اطلاعات Drive را انجام داد.

این Interrupt برای داستان Boot اهمیت زیادی دارد، چون Boot Codeهای قدیمی برای ادامه خواندن اطلاعات از Disk می‌توانند از سرویس‌های BIOS Disk استفاده کنند.

Interrupt معروف دیگری INT 16h برای سرویس‌های Keyboard BIOS استفاده می‌شود.

INT 10h    ; Video BIOS
INT 13h    ; Disk BIOS
INT 16h    ; Keyboard BIOS

این سرویس‌ها یک لایه نرم‌افزاری میان برنامه و جزئیات سخت‌افزار ایجاد می‌کنند.

آیا BIOS چیزی شبیه API داشت؟

از دید مفهومی می‌توان چنین نگاهی داشت. البته BIOS Interrupt Services دقیقاً همان چیزی نیست که امروز با عنوان API می‌شناسیم، اما ایده مشابه است.

برنامه لازم نیست برای هر عملیات ساده مستقیماً Registerها و Controllerهای سخت‌افزار را مدیریت کند. می‌تواند یک سرویس مشخص Firmware را فراخوانی کند.

Application
     |
     v
BIOS Interrupt Service
     |
     v
BIOS Routine
     |
     v
Hardware

پس Interruptهای DOS چه هستند؟

اینجا باید تفاوت مهمی را مشخص کنیم. Interruptهایی مانند INT 10h، INT 13h و INT 16h به سرویس‌هایی مربوط می‌شوند که BIOS فراهم می‌کند.

اما اگر برنامه‌های DOS Assembly را دیده باشید، احتمالاً بارها با:

INT 21h

برخورد کرده‌اید.

INT 21h سرویس BIOS نیست. این Interrupt رابط اصلی بسیاری از سرویس‌های سیستم‌عامل DOS است.

قبل از اینکه DOS اجرا شود، INT 21h هنوز نقش معروف خود را به‌عنوان رابط سرویس‌های DOS ندارد. ابتدا Firmware سیستم را راه‌اندازی می‌کند، سپس Boot Code اجرا می‌شود و بعد DOS بارگذاری و اجرا می‌شود.

DOS در جریان Initialization، Interrupt Vectorهای لازم را تنظیم می‌کند تا برنامه‌ها بتوانند سرویس‌های سیستم‌عامل را فراخوانی کنند.

BIOS و DOS دو لایه متفاوت‌اند

Application
     |
     | INT 21h
     v
    DOS
     |
     | may use BIOS services
     v
   BIOS
     |
     v
 Hardware

این دیاگرام نباید به این معنی برداشت شود که تمام عملیات DOS الزاماً از BIOS عبور می‌کنند. DOS، Driverها و نرم‌افزارهای مختلف بسته به نوع عملیات می‌توانند مسیرهای متفاوتی برای دسترسی به سخت‌افزار داشته باشند.

INT 21h چه کارهایی انجام می‌دهد؟

DOS تعداد زیادی سرویس را از طریق INT 21h در اختیار برنامه‌ها قرار می‌دهد.

از جمله عملیات مرتبط با:

Console I/O
Keyboard input
File operations
Directory operations
Memory management
Program execution
Program termination

خود INT 21h تنها یک Function نیست. معمولاً یک Register، اغلب AH، شماره Function موردنظر را مشخص می‌کند. سپس برنامه INT 21h را اجرا می‌کند.

در نتیجه یک Interrupt می‌تواند دروازه ورود به مجموعه بزرگی از سرویس‌های سیستم‌عامل باشد.

چرا Interruptها برای بحث حافظه مهم‌اند؟

چون Interruptها فقط یک مفهوم انتزاعی در CPU نیستند. CPU باید بداند Handler مربوط به هر Interrupt در کجا قرار دارد.

آدرس این Handlerها در Interrupt Vector Table ذخیره می‌شود و IVT خودش در اولین یک کیلوبایت حافظه قرار دارد.

Firmware هنگام Initialization تعدادی از Vectorها را برای سرویس‌ها و Handlerهای لازم تنظیم می‌کند. بعداً سیستم‌عاملی مثل DOS نیز می‌تواند Vectorهای مربوط به خودش را تنظیم کند یا بعضی Vectorهای موجود را تغییر دهد.

Driverها و برنامه‌های خاص نیز در بعضی شرایط می‌توانند Interrupt Vectorها را Hook کنند. بنابراین همان یک کیلوبایت کوچک ابتدای حافظه نقش مهمی در ارتباط بین CPU، Firmware، DOS، Driverها و Applicationها دارد.

بعد از IVT چه چیزی قرار دارد؟

بعد از Interrupt Vector Table یکی دیگر از ساختارهای مهم PCهای کلاسیک قرار دارد: BIOS Data Area یا BDA.

BDA از آدرس فیزیکی:

0x00400

شروع می‌شود. BIOS اطلاعات مختلفی درباره وضعیت سیستم را در این قسمت نگهداری می‌کند. برای مثال اطلاعاتی مربوط به Keyboard، Video Mode، بعضی Portها، تجهیزات سیستم و مقدار Base Memory در این ناحیه نگهداری می‌شود.

0x00000
+-----------------------------+
| Interrupt Vector Table      |
|          1 KB               |
+-----------------------------+
0x00400
| BIOS Data Area              |
+-----------------------------+

|                             |
| Remaining Conventional RAM  |
|                             |

پس Conventional Memory از نظر فیزیکی از آدرس صفر شروع می‌شود، اما تمام آن برای Applicationها آزاد نیست.

وقتی DOS بالا می‌آید چه اتفاقی برای 640KB می‌افتد؟

DOS خودش باید در حافظه قرار بگیرد. Driverها نیز حافظه لازم دارند. Command Interpreter و ساختارهای داخلی سیستم‌عامل هم بخشی از حافظه را اشغال می‌کنند.

برنامه‌های TSR یا Terminate and Stay Resident نیز می‌توانند بعد از پایان اجرای ظاهری خود، بخشی از حافظه را اشغال نگه دارند.

0 KB
+-----------------------------+
| IVT                         |
| BIOS Data Area              |
| DOS                         |
| Drivers                     |
| TSR Programs                |
|                             |
| Free Conventional Memory    |
|                             |
| Application                 |
|                             |
+-----------------------------+
640 KB

به همین دلیل مقدار Free Conventional Memory در دوران DOS اهمیت زیادی داشت. ممکن بود یک کامپیوتر چند مگابایت RAM داشته باشد، اما یک بازی یا برنامه DOS اجرا نشود چون فضای کافی در محدوده Conventional Memory باقی نمانده بود.

چطور ممکن است چند مگابایت RAM داشته باشیم ولی فقط 640KB این‌قدر مهم باشد؟

با آمدن پردازنده‌های جدیدتر x86، CPU توانست بسیار بیشتر از یک مگابایت حافظه را آدرس‌دهی کند. اما DOS و تعداد زیادی از برنامه‌های Real Mode همچنان با محدودیت‌ها و ساختار تاریخی معماری PC سازگار بودند.

حافظه بالاتر از مرز یک مگابایت بعدها با مفاهیمی مانند Extended Memory و روش‌های مختلف مدیریت حافظه مورد استفاده قرار گرفت. همچنین روش‌هایی برای استفاده بهتر از بعضی نواحی Upper Memory ایجاد شدند.

اما برای فهم Boot اولیه و معماری Real Mode، همان نقشه یک مگابایتی مهم‌ترین نقطه شروع است.

حالا نقشه کامل‌تری از حافظه داریم

0x00000
+--------------------------------+
| Interrupt Vector Table         |
| 256 vectors × 4 bytes          |
+--------------------------------+
0x00400
| BIOS Data Area                 |
+--------------------------------+
|                                |
| DOS / Drivers / Programs       |
|                                |
| Conventional Memory            |
|                                |
+--------------------------------+
0xA0000         640 KB

|                                |
| Video Memory                   |
| Video BIOS                     |
| Option ROMs                    |
| Hardware Areas                 |
|                                |

0xF0000
+--------------------------------+
| System BIOS                    |
|                                |
| Reset Vector near 0xFFFF0      |
+--------------------------------+
0xFFFFF          1 MB

حالا Reset Vector مقاله قبل دیگر یک آدرس تنها و بدون زمینه نیست. در بالای اولین مگابایت Firmware و Reset Vector را می‌بینیم.

در پایین حافظه Interrupt Vector Table و BIOS Data Area قرار دارند. و میان آنها Conventional Memory قرار گرفته است که DOS، Driverها و Applicationها از آن استفاده می‌کنند.

از روشن شدن سیستم تا برنامه DOS

اگر دو مقاله را کنار هم قرار دهیم، مسیر کلی تا اینجا تقریباً چنین است:

Power On
   |
   v
CPU Reset
   |
   v
Reset Vector
   |
   v
Firmware / BIOS
   |
   v
Hardware Initialization
   |
   v
BIOS Services Available
   |
   v
Boot Device
   |
   v
Boot Code
   |
   v
DOS
   |
   v
DOS Services
   |
   v
Application

در این تصویر، دستورهایی مثل INT 10h، INT 13h، INT 16h و INT 21h دیگر دستورهای جادویی Assembly نیستند.

پشت هرکدام یک Interrupt Vector، یک Handler و بخشی از Software سیستم وجود دارد. و مسیر رسیدن CPU به آن Handler از همان جدولی آغاز می‌شود که در ابتدای حافظه قرار دارد: Interrupt Vector Table.

جمع‌بندی

Real Mode فقط یک روش قدیمی برای آدرس‌دهی حافظه نیست. این مدل پایه بسیاری از مفاهیم مهم PCهای کلاسیک را شکل می‌دهد.

با Segment:Offset، یک پردازنده 16 بیتی مانند 8086 می‌تواند فضای آدرس فیزیکی تقریباً یک مگابایتی خود را مدیریت کند.

در معماری PC این فضا به‌طور کلی به دو ناحیه معروف تقسیم شد:

640 KB Conventional Memory

384 KB Upper Memory Area

اما حتی Conventional Memory نیز کاملاً در اختیار Application نیست. اولین یک کیلوبایت حافظه محل Interrupt Vector Table است و بعد از آن BIOS Data Area قرار دارد.

Firmware از سرویس‌هایی مانند INT 10h، INT 13h و INT 16h برای ارائه امکانات مختلف به نرم‌افزار استفاده می‌کند.

و پس از بارگذاری DOS، سرویس‌هایی مانند INT 21h به برنامه‌ها اجازه می‌دهند از امکانات سیستم‌عامل استفاده کنند.

Application
     |
     v
Operating System / DOS
     |
     v
Firmware / BIOS
     |
     v
Hardware

حافظه و Interruptها از مهم‌ترین نقاط اتصال میان این لایه‌ها هستند.

مقاله قبلی مقاله بعدی بازگشت به مقالات صفحه اصلی