تصور کنید تمام منطق یک بازی پیچیده مثل شطرنج، در فضایی کمتر از یک پاراگراف متنی جای بگیرد. این دقیقاً همان چیزی است که AttoChess به دست آورده است تا مرزهای بهرهوری سختافزاری را جابهجا کند.
به نقل از مستندات پروژه، این برنامه که در اوایل ۲۰۲۶ توسط نیکولاس تانر (Nicholas Tanner) منتشر شد، رکورد جدیدی را برای کوچکترین موتور شطرنج قابل اجرا روی سختافزار ۱۶ بیتی DOS ثبت کرد. این برنامه یک حلقهٔ کامل و قابل بازی است؛ یعنی بوت میشود، وضعیت صفحه را رسم میکند، حرکت کاربر را از کیبورد میگیرد، تا چهار لایه (ply) آینده را با روش بازگشتی جستوجو میکند و در نهایت یک حرکت قانونی را به عنوان پاسخ برمیگرداند. عدد ۲۷۸ بایت، اندازهٔ نهایی فایل اجرایی .COM است که بایتبهبایت در فایل تولید شده تأیید شده است.
این دستاورد حاصل دههها تلاش در حوزهای است که به آن «کد گلف» (Code Golf) — یعنی هنر نوشتن برنامهای با کمترین تعداد کاراکتر ممکن — میگویند. برای بیش از سی سال، برنامه 1K ZX Chess (۶۷۲ بایت، ۱۹۸۲) اثر دیوید هورن، استاندارد طلایی مینیمالیسم روی سیستمهای Sinclair ZX81 بود. سپس در سال ۲۰۱۵، اولیویه پوداد با برنامه BootChess (۴۸۷ بایت) این سقف را پایین آورد و آن را در یک بخش بوت (boot sector) x86 جای داد. طبق گزارشهای تخصصی، LeanChess (۲۸۸ بایت، ۲۰۱۹) اثر دیمیتری شختمن، رقابت را به فایلهای .COM در محیط x86 DOS منتقل کرد. LeanChess با معرفی صفحه به سبک 0x88 و جستوجوی بازگشتی مینیمکس، زیرساختی را ایجاد کرد که AttoChess اکنون آن را بهینهتر کرده است.
همانطور که در تحلیلهای پیشین ما دربارهی بهینهسازیهای سطح پایین و معماری پردازندهها اشاره کردیم، حذف هر بایت اضافی نیازمند بازنگری در بنیادیترین مفاهیم است.
معماری AttoChess
برخلاف برخی پروژههای نمایشی، AttoChess صرفاً یک نمایش بصری یا برنامهای که فقط صفحه را میچیند نیست، بلکه یک موتور کاربردی است. این برنامه یک نسخه بهینهشده از نظر اندازه از LeanChess است و به عنوان یک اثر مشتق شده، کپیرایت و مجوز MIT شختمن را بهطور کامل حفظ کرده است. موتور این بازی از جستوجوی بازگشتی مینیمکس استفاده میکند و در محیطی با محدودیت حافظه شدید عمل میکند.
تراشیدن ۱۰ بایت اضافی نسبت به LeanChess، از طریق بازنگری در محیط اطراف (periphery) برنامه بهجای تغییر در منطق هسته (core logic) رخ داده است. تمام این ده بایت صرفهجویی از بازنگری در نحوه رسم صفحه، نحوه رمزگشایی حرکات و یک تغییر داخلی خاص در حلقهٔ جستوجو برای آزاد کردن یک ثبات (Register) به دست آمده است.
بهینهسازیهای نمایش
یکی از بزرگترین صرفهجوییها از طریق حذف کامل بافر رندر (Render Buffer) حاصل شده است. در LeanChess، وضعیت صفحه ابتدا در یک بافر مجزا رندر میشد؛ به این صورت که هر خانه به یک کاراکتر قابل چاپ تبدیل شده، یک پایاندهنده $ به آن اضافه میشد و سپس رشته با استفاده از تابع 09h در DOS (توسط int 21h) چاپ میگشت. این فرآیند نیازمند تنظیم اشارهگر بافر، یک حلقه کپی، نوشتن کاراکتر پایاندهنده و رزرو یک آرایه صفحه در تصویر فایل بود.
AttoChess تمام این موارد را حذف کرده است. ستونهای حاشیهٔ صفحه اکنون بهجای پرکنندههای 08h، به صورت CR، LF، CR، LF (کدهای 0Dh، 0Ah، 0Dh، 0Ah) چیده شدهاند. از آنجا که هر دو کاراکتر CR و LF دارای بیت ۳ فعال هستند، آنها از تستهای ماسک رنگ و حاشیه موجود عبور میکنند. حلقهٔ نمایش اکنون بایتها را مستقیماً از طریق int 29h (خروجی سریع کنسول DOS) به نمایشگر میفرستد.
- حاشیهها بهطور خودکار به خطوط جدید تبدیل میشوند.
- خانههای خالی به صورت NUL ارسال میشوند.
- فقط مهرههای واقعی تحت تبدیل ASCII قرار میگیرند (با استفاده از آفست 4Bh برای نگاشت به حروف K, N, B, P, Q, R بسته به رنگ مهره).
- بافر رندر، تنظیمات اشارهگر، کاراکتر $ و آرایه رزرو شده صفحه بهطور کامل از تصویر فایل حذف شدهاند.
این منطق از طریق حلقهای اجرا میشود که از main_loop شروع شده و صفحه را از آدرس board_db + 24 بارگذاری میکند. این حلقه ۹۸ بایت (۸ ردیف شطرنج بهعلاوه یک CR,LF نهایی) را پیمایش میکند. در داخل disp_loop ،بایت مورد نظر برای شناسایی مهره با دستور test al, 30h بررسی میشود. اگر مهره باشد، نوع و رنگ آن با and al, 27h ایزوله شده و سپس مقدار 4Bh به آن اضافه میشود تا کاراکتر نهایی پیش از فراخوانی int 29h تولید گردد.
منطق ورودی و راهاندازی
AttoChess همچنین دستورات تنظیم حالت BIOS (int 10h) را که در نسخه پیشین برای اجبار به حالت نمایش ۰ به کار میرفت، حذف کرد. چون دستور int 29h مستقل از حالت نمایش (mode-independent) است، برنامه فقط دو فرض اولیه در لحظه ورود میکند: پاکسازی پرچم جهت (cld) — زیرا تضمینی نیست که DF در بدو ورود پاک باشد — و تنظیم تعداد ردیفها روی ۱۳ (mov cx, 13)، چرا که مقدار CX در لحظه ورود تضمینشده نیست.
رمزگشایی ورودی نیز برای صرفهجویی در فضا سادهسازی شد. خواندن یک حرکت نیازمند تبدیل دو کاراکتر تایپ شده (ستون و ردیف) به یک آدرس در صفحه است. LeanChess این کار را در مراحل مجزا انجام میداد: خواندن ستون، خواندن ردیف، ماسک کردن ردیف با and al, 0Fh و استفاده از ضرب برای یافتن آفست ردیف.
AttoChess این محاسبات ریاضی را با ادغام مستقیم ثابتهای بایاس ASCII در آدرس پایه حذف کرد: mov di, offset board_db + 123 + 0CE0h. سپس اجازه میدهد ریاضیات اشارهگر ۱۶ بیتی به صورت mod 64K بچرخد (wrap around). مرحله نرمالسازی و تنظیمات جداگانه ضرب حذف شده و با یک دستور تکخطی imul ax, 12 (یک ضرب در فرم immediate مربوط به 80186) جایگزین شده تا مستقیماً روی خانه هدف قرار گیرد. این دستور خاص جایگزین جفت دستورات قبلی mov ah, 12 و mul ah شده است.
جستوجو و منطق پیادهها
در داخل جستوجوی بازگشتی، AttoChess ثبات CX را آزاد کرد. LeanChess از یک حلقه شمارشی برای اسکن خانههای منبع کاندید استفاده میکرد (mov cl, 92 ... loop src_loop) که باعث تخریب عمق جستوجوی ذخیره شده در CX میشد. این یعنی برنامه مجبور بود در هر فراخوانی بازگشتی، عمق را دوباره از پشته بخواند (mov cx, [si + 32]).
AttoChess مکانیسم پیمایش خانههای منبع را به مقایسه اشارهگر با انتهای صفحه تغییر داد: cmp bp, offset board_db + 120. این تغییر اجازه میدهد CX در تمام طول اسکن به عنوان شمارنده زنده عمق باقی بماند. در نتیجه، محل فراخوانی بازگشتی میتواند صرفاً از dec cx استفاده کند و نیاز به بارگذاری مجدد از پشته بهطور کامل حذف شود.
حرکات پیادهها نیز برای حذف جابهجاییهای بیتی (bit-shuffling) سادهسازی شد. منطق اصلی سابق، بیت علامت بردار را ایزوله کرده، آن را شیفت میداد و با رنگ نوبت حرکت XOR میکرد تا جهت را تعیین کند. AttoChess تست «جلو-عقب» را مستقیماً با یک عملیات xor al, dh در بیت ۵ رنگ ادغام میکند. همچنین از زوجیت بردار برای تشخیص ضربات (captures) از پیشرویها (pushes) استفاده میکند.
- تشخیص قطری: AttoChess حرکات قطری را با شناسایی آفستهای فرد (مانند ۱۱+ یا ۱۳-) تشخیص میدهد.
- منطق ضربه: اگر حرکت قطری باشد، حتماً باید ضربه باشد؛ در غیر این صورت، یک پیشروی ساده است.
- ادغام رنگ: چون تست جهت بر اساس رنگ نوبت حرکت است و نه یک علامت مطلق، هر دو رنگ از یک مسیر کد مشترک استفاده میکنند.
- تست فضای خالی: برای حرکات مستقیم (۱۲+ یا ۱۲-)، کد رنگ مقصد را با
xor ah, 30hمعکوس میکند تا تست خالی بودن خانه را انجام دهد.
محدودیتهای فنی و محدوده
برای حفظ این اندازه بسیار کوچک، موتور برخی معاوضههای خاص را پذیرفته است که از اجدادش به ارث رسیده است. این برنامه حرکات استاندارد مهرهها و ضربات را با بازگشت واقعی ۴ لایه (4-ply recursion) پیاده میکند، اما چندین قانون پیشرفته را ندارد:
- عدم وجود قلعهرفتن (Castling): حرکت ویژه شاه و رخ پیادهسازی نشده است.
- عدم وجود زوبزدن (En Passant): ضربات باید مستقیم باشند.
- عدم ترفیع پیاده: پیادهها هنگام رسیدن به انتهای صفحه، پیاده باقی میمانند.
- داوری سادهشده: منطق کامل کیش و مات وجود ندارد؛ طرفی که هیچ حرکت قانونی ندارد و امتیاز او صفر یا مثبت است، صرفاً متوقف میشود.
ورودیها بهطور کامل مورد اعتماد فرض میشوند. موتور انتظار مختصات درست (مثلاً "e2e4") را دارد و پیش از اجرا، حرکات را از نظر قانونی بودن اعتبارسنجی نمیکند. این محدودیتها برای اینکه تعداد بایتها یک رکورد معنادار باقی بماند، ضروری هستند.
ساخت و تأیید
برای تأیید این ادعاها، کد باید اسمبل و اندازهگیری شود. برنامه با سینتکس MASM/TASM برای هدف پردازنده 80186 نوشته شده است. استفاده از Turbo Assembler (TASM) و TLINK با سوئیچ /t منجر به تولید یک فایل .COM بهجای .EXE میشود.
مراحل اجرا به شرح زیر است:
۱. اسمبل: tasm AttoChess
۲. لینک: tlink /t AttoChess
۳. تأیید: دستور ls -l AttoChess.com اندازه دقیق ۲۷۸ بایت را تأیید میکند.
هیچ چیز دیگری در تصویر فایل وجود ندارد: نه پدینگ در بخش دادهها، نه بافر رندر مجزا و نه آرایه رزرو شده صفحه. هر بایت در AttoChess.com یا کد کاربردی است یا دادههای جدولی که برنامه واقعاً از آنها استفاده میکند.
کاربران میتوانند برنامه را در هر محیط ۱۶ بیتی DOS مانند DOSBox، PCem یا 86Box اجرا کنند، زیرا فقط به فراخوانیهای استاندارد int 21h و int 29h متکی است. برای کسانی که از DOSBox استفاده میکنند، فرآیند شامل مونت کردن دایرکتوری (mount c: .) و سپس اجرای AttoChess.com است. در یک جلسه معمولی، بازیکن در نقش سفید است و کامپیوتر پس از ورود حرکت چهارکاراکتری توسط کاربر، بهطور خودکار در نقش سیاه پاسخ میدهد. صفحه پس از هر جفت حرکت بازترسیم میشود.
این پروژه ثابت میکند که حتی در عصر مدلهایی با میلیاردها پارامتر، تلاش برای بهرهوری حداکثری در زبان اسمبلی همچنان بینشهای جدیدی درباره نحوه استفاده از سختافزار و تراکم الگوریتمیک ارائه میدهد. تانر با حذف هر بایت غیرضروری، کف مطلق آنچه را که یک موتور شطرنج «قابل بازی» میکند، ترسیم نموده است.
گام بعدی شما
- اگر به برنامهنویسی سطح پایین علاقه دارید، سعی کنید کد AttoChess را در DOSBox اجرا کرده و با تغییرات کوچک در ثباتها، رفتار موتور را تغییر دهید.
- مستندات LeanChess را مطالعه کنید تا تفاوتهای معماری بین این دو نسخه را بهتر درک کنید.
- درباره مفهوم «کد گلف» در زبانهای مدرنتر تحقیق کنید تا ببینید چطور میتوان منطقهای پیچیده را در کمترین فضای ممکن جای داد.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما درباره تراشههای نسل جدید و بهینهسازیهای سختافزاری مراجعه کنید.




گفتگو