| RGBASM(5) | File Formats Manual | RGBASM(5) |
توضیحات (DESCRIPTION)
این توضیحات کامل زبان اسمبلی مورد استفاده توسط rgbasm(1) است. برای توضیحات کامل دستورالعملهای زبان ماشین پشتیبانیشده توسط پردازنده Game Boy، به gbz80(7) مراجعه کنید.
توصیه میشود پیش از خواندن این سند، تا حدی با سختافزار Game Boy آشنایی داشته باشید. RGBDS بهطور ویژه برای Game Boy هدفگذاری شده است، بنابراین بسیاری از ویژگیهای آن مستقیماً با مفاهیم سختافزاری آن گره خوردهاند. این سند به عنوان مرجع سختافزار Game Boy در نظر گرفته نشده است.
بهطور کلی، “پیونددهنده” به rgblink(1) اشاره دارد، اما هر برنامهای که فایلهای شیء RGBDS (توضیح داده شده در rgbds(5)) را پردازش کند، میتواند به جای آن استفاده شود.
ساختار دستوری (SYNTAX)
ساختار دستوری، درست مانند هر اسمبلر دیگری، مبتنی بر خط است. هر خط میتواند شامل بخشهایی به یکی از این دو ترتیب باشد:
- [label:] [directive] [; comment]
- [label:] [instruction [:: instruction ...]] [; comment]
شبهدستورها
(Directives)
فرمانهایی
به خود
اسمبلر
هستند،
مانند PRINTLN
، SECTION ، یا
OPT.
برچسبها
یک نام را به
مکانی مشخص
درون یک بخش
پیوند
میدهند (به
Labels در
ادامه
مراجعه
کنید).
برچسبها
قبل از اکثر
شبهدستورها
مجاز
هستند، اما
قبل از IF
، ELIF ، ELSE
، ENDC ، REPT
، FOR ، ENDR
، MACRO ، یا
ENDM مجاز
نیستند.
دستورالعملها
به
آپکدهای Game Boy
ترجمه
(اسمبل)
میشوند.
چندین
دستورالعمل
در یک خط، و
همچنین
شبهدستورهای
داده (به
Defining constant data
in ROM در ادامه
مراجعه
کنید)،
میتوانند
با دو
دونقطه
‘::’ از
یکدیگر جدا
شوند.
دستورالعملهای موجود در gbz80(7) مستند شدهاند.
توجه
داشته
باشید در
جایی که یک
دستورالعمل
به یک ثبات ۸
بیتی r8
نیاز دارد،
میتواند
HIGH(r16) را
به عنوان
ثبات ۸ بیتی
بالایی از
r16
دادهشده
تفسیر کند؛
برای مثال،
HIGH(HL)
برای H ؛ و
LOW(r16) را
به عنوان
ثبات
پایینی
تفسیر کند؛
برای مثال،
LOW(HL)
برای L
(بهجز برای
LOW(AF ،)
زیرا F یک
ثبات معتبر
نیست).
همچنین
توجه داشته
باشید در
جایی که یک
دستورالعمل
به یک کد
وضعیت cc
نیاز دارد،
میتواند
!cc را به
عنوان کد
وضعیت
معکوس
تفسیر کند؛
برای مثال،
!nz برای
z.
تمام کلیدواژههای رزروشده (شبهدستورها، دستورالعملها، ثباتها، توابع داخلی و غیره) به حروف بزرگ و کوچک حساس نیستند (case-insensitive)؛ تمام شناسهها (برچسبها، متغیرها و غیره) به حروف بزرگ و کوچک حساس هستند (case-sensitive).
توضیحات (Comments) برای ارائه اطلاعاتی درباره کد به انسانها، مانند توضیحات تکمیلی، استفاده میشوند. اسمبلر همیشه توضیحات و محتوای آنها را نادیده میگیرد.
دو نوع
توضیح وجود
دارد:
درونخطی و
بلوکی.
توضیحات
درونخطی
هر چیزی
هستند که پس
از یک
نقطهویرگول
‘;’ که
درون یک
رشته
نباشد، تا
انتهای خط
قرار
میگیرند.
توضیحات
بلوکی، که
با ‘/*’
آغاز شده و
با ‘*/’
پایان
مییابند،
میتوانند
در چندین خط
پخش شوند یا
در میان یک
عبارت قرار
گیرند.
مثالی که این ویژگیهای ساختار دستوری را نشان میدهد:
SECTION "My Code", ROM0 ; a directive
MyFunction: ; a label
push hl ; an instruction
/* ...and multiple instructions,
with mixed case */
ld a, [hli] :: LD H, [HL] :: Ld l, a
pop /*wait for it*/ hl
ret
گاهی اوقات خطوط ممکن است بسیار طولانی شوند و شکستن آنها ضروری باشد. برای این کار، یک بکاسلش در انتهای خط قرار دهید:
DB 1, 2, 3, \
4, 5, 6, \ ; Put it before any comments
7, 8, 9
DB "Hello, \ ; Space before the \ is included
world!" ; Any leading space is included
درج نماد (Symbol interpolation)
نمادهایی
با مقادیر
رشتهای یا
عددی
میتوانند
با نوشتن
آنها درون
‘{braces}’
“درج” شوند
(interpolated). این کار
محتوای
نماد را
طوری
جایگذاری
میکند که
گویی بخشی
از فایل
منبع
بودهاند.
اگر یک نماد
رشتهای
باشد،
نویسههای
آن به همان
صورت درج
میشوند.
اگر یک نماد
عددی باشد،
مقدار آن به
نمادگذاری
هگزادسیمال
با یک علامت
دلار ‘$’ در
ابتدا
تبدیل
میشود.
درج نمادها میتواند تودرتو نیز باشد.
DEF topic EQUS "life, the universe, and \"everything\""
DEF meaning EQUS "answer"
; Defines answer = 42
DEF {meaning} = 42
; Prints "The answer to life, the universe, and "everything" is $2A"
PRINTLN "The {meaning} to {topic} is {{meaning}}"
PURGE topic, meaning, {meaning}
نمادها را
میتوان
حتی در
زمینههایی
که بسط
خودکار
ثابتهای
رشتهای را
غیرفعال
میکنند
درج کرد:
یعنی
‘name’ در
تمام موارد
‘DEF({name}) ،’
‘DEF {name} EQU/=/EQUS/etc ...
،’ ‘REDEF {name} EQU/=/EQUS/etc
... ،’ ‘FOR {name}, ...
،’ ‘PURGE {name}
،’ و ‘MACRO
{name}’ بسط
داده خواهد
شد، هرچند
در ‘DEF(name) ،’
‘PURGE {name}’ و
غیره بسط
داده
نخواهد شد.
امکان
تغییر نحوه
چاپ نمادها
با مشخص
کردن قالب
چاپ به این
صورت وجود
دارد:
‘{fmt:symbol}’.
مشخصکننده
‘fmt’ از
بخشهایی
تشکیل شده
که باید به
ترتیب زیر
باشند:
‘<sign><exact><align><pad><width><frac><prec><type>’.
تمام این
بخشها
اختیاری
هستند،
بهجز
‘<type>’ که
الزامی است.
این بخشها
عبارتند
از:
| Part | Meaning |
‘<sign>’ |
میتواند
‘+’ یا
‘باشد.’
در صورت
تعیین، این
نویسه را در
جلوی اعداد
غیرمنفی
چاپ
میکند. |
‘<exact>’ |
میتواند
‘#’ باشد
(تنها برای
انواع
غیردهدهی
مجاز است).
در صورت
تعیین،
مقدار را در
قالبی
«دقیق» چاپ
میکند: با
یک پیشوند
مبنا (‘$’ ،
‘&’ ، یا ‘%’)
برای انواع
صحیح
غیردهدهی
(‘x’ / ‘X’ ، ‘o’ ،
یا ‘b’ ؛) با
یک پسوند
دقت ‘q’
برای اعداد
ممیز ثابت؛
یا با
نویسههای
گریز ‘\’
(اما بدون
گیومههای
محصورکننده)
برای
رشتهها. |
‘<align>’ |
میتواند
‘-’
باشد. در
صورت
تعیین، به
جای راست،
از چپ تراز
میکند. |
‘<pad>’ |
میتواند
‘0’
باشد. در
صورت
تعیین،
اعداد
راستتراز
را به جای
فاصله با
صفر پر
میکند. |
‘<width>’ |
میتواند
یک یا چند
نویسه
‘0’ –
‘9’
باشد. در
صورت
تعیین،
مقدار را تا
این پهنا پر
میکند که
بهطور
پیشفرض با
فاصله از
راست تراز
میشود. |
‘<frac>’ |
میتواند
‘.’ باشد
که با صفر
یا چند
نویسه
‘0’ –
‘9’
دنبال
میشود. در
صورت
تعیین، این
تعداد
ارقام کسری
از یک عدد
ممیز ثابت
را چاپ
میکند.
بهطور
پیشفرض ۵
رقم است و
حداکثر ۲۵۵
رقم
میباشد.
(یک ‘.’
که با صفر
رقم ‘0’
– ‘9’
دنبال شود،
صفر رقم
کسری و بدون
ممیز
اعشاری چاپ
میکند.) |
‘<prec>’ |
میتواند
‘q’ باشد
که با یک یا
چند نویسه
‘0’ –
‘9’
دنبال
میشود. در
صورت
تعیین، یک
عدد ممیز
ثابت را با
این دقت چاپ
میکند.
بهطور
پیشفرض
روی گزینه
جاری -Q
تنظیم است. |
‘<type>’ |
نوع مقدار را مشخص میکند. |
انواع معتبر عبارتند از:
| Type | Format | Example |
‘d’ |
دهدهی علامتدار | -42 |
‘u’ |
دهدهی بدون علامت | 4294967254 |
‘x’ |
هگزادسیمال با حروف کوچک | 2a |
‘X’ |
هگزادسیمال با حروف بزرگ | 2A |
‘b’ |
باینری (دودویی) | 101010 |
‘o’ |
اکتال (هشتهشتی) | 52 |
‘f’ |
ممیز ثابت | 1234.56789 |
‘s’ |
رشته | string contents |
مثالها:
SECTION "Test", ROM0[2]
X: ; This works with labels **whose address is known**
DEF Y = 3 ; This also works with variables
DEF SUM EQU X + Y ; And likewise with numeric constants
; Prints "%0010 + $3 == 5"
PRINTLN "{#05b:X} + {#x:Y} == {d:SUM}"
rsset 32
DEF PERCENT rb 1 ; Same with offset constants
DEF VALUE = 20
DEF RESULT = MUL(20.0, 0.32)
; Prints "32% of 20 = 6.40"
PRINTLN "{d:PERCENT}% of {d:VALUE} = {f:RESULT}"
DEF WHO EQUS STRLWR("WORLD")
; Prints "Hello world!"
PRINTLN "Hello {s:WHO}!"
اگرچه
برای این
مثالها،
STRFMT
مناسبتر
خواهد بود؛
به String
expressions در ادامه
مراجعه
کنید.
عبارات (EXPRESSIONS)
دو نوع عبارت وجود دارد: عددی و رشتهای.
عبارات عددی همیشه با استفاده از محاسبات ۳۲ بیتی علامتدار ارزیابی میشوند. در زمینههای منطق بولی، صفر تنها عدد «نادرست» (false) در نظر گرفته میشود و تمام اعداد غیرصفر (از جمله منفی) «درست» (true) هستند.
یک عبارت
«ثابت»
نامیده
میشود اگر
مقدار
آن را بداند.
این عموماً
همیشه صادق
است، مگر
اینکه
برچسبی در
میان باشد،
همانطور
که در بخش
SYMBOLS
توضیح داده
شده است. با
این حال،
برخی از
عملگرها
حتی با
عملوندهای
غیرثابت
نیز
میتوانند
ثابت
باشند،
همانطور
که در
Operators در
ادامه
توضیح داده
شده است.
شبهدستورها
معمولاً
نیازمند
عبارات
ثابت هستند:
برای مثال،
REPT نیاز
دارد که
تعداد
تکرارها در
زمان ترجمه
کد (assembly time) مشخص
باشد.
لیترالهای عددی (Numeric literals)
از
انواع
مختلف
لیترالهای
عددی
پشتیبانی
میکند.
| Format type | Prefixes | Accepted characters |
| دهدهی | ندارد | 0123456789 |
| هگزادسیمال | $,
0x, 0X |
0123456789ABCDEF |
| اکتال | &,
0o, 0O |
01234567 |
| باینری | %,
0b, 0B |
01 |
| ممیز ثابت | ندارد | 01234.56789 |
| ممیز ثابت دقیق | ندارد | 12.34q8 |
| ثابت نویسهای | ندارد | 'A' |
| گرافیک Game Boy | ` |
0123 |
خط زیرین (_)
نیز در
اعداد
پذیرفته
میشود،
بهجز در
ابتدای عدد.
این
میتواند
برای
گروهبندی
ارقام مفید
باشد،
مانند
‘123_456’ یا
‘%1100_1001’.
شکل «ثابت نویسهای» مقداری را به دست میدهد که نویسه در جدول نگاشت نویسه (charmap) جاری به آن نگاشت شده است. برای مثال، بهطور پیشفرض (به ascii(7) مراجعه کنید) ‘'A'’ مقدار ۶۵ را به دست میدهد. یک ثابت نویسهای باید یک مقدار واحد را نشان دهد، بنابراین نمیتواند شامل چندین نویسه یا نویسههایی باشد که به چندین مقدار نگاشت میشوند. برای اطلاعات درباره نگاشتهای نویسه به Character maps و برای اطلاعات درباره نویسههای گریز مجاز در ثابتهای نویسهای به String expressions مراجعه کنید.
مورد آخر، گرافیک Game Boy، حداکثر تا هشت رقم بین ۰ و ۳ را انتظار دارد که متناظر با مقادیر سایه دو بیتی پیکسلها هستند. مقدار عددی حاصل، دو بایت داده تایل است که آن سطر از پیکسلها را تولید میکند. برای مثال، ‘`01012323’ معادل ‘$0F55’ است.
به جای یک لیترال عددی، میتوانید از نام یک نماد عددی نیز استفاده کنید که بهطور ضمنی با مقدار آن جایگزین میشود.
عملگرها (Operators)
میتوانید از این عملگرها در عبارات عددی استفاده کنید (فهرستشده از بیشترین به کمترین اولویت):
| Operator | Meaning |
( ) |
گروهبندی |
FUNC() |
فراخوانی تابع داخلی |
** |
به توان رساندن |
+ - ~ ! |
مثبت یگانی، منفی یگانی (قرینهسازی)، متمم (نقیض بیتی)، و نقیض بولی |
* / % |
ضرب، تقسیم (گرد کردن به پایین)، و مدول (باقیمانده) |
<< >>
>>> |
شیفتهای بیتی (به چپ، به راست با بسط علامت، به راست با بسط صفر) |
& | ^ |
عملگرهای بیتی AND/OR/XOR |
+ - |
جمع و تفریق |
== != < > <=
>= |
مقایسهها |
&& |
عملگر بولی AND |
|| |
عملگر بولی OR |
‘**’ عددی
را به توان
غیرمنفی
میرساند.
این تنها
عملگر
راستتداعی
است، به این
معنی که ‘p
** q ** r’ برابر
است با ‘p ** (q
** r) ،’ نه ‘(p
** q) ** r’. تمام
عملگرهای
دوگانی
دیگر
چپتداعی
هستند.
‘~’ یک مقدار را با معکوس کردن تمام ۳۲ بیت آن متمم میکند.
‘%’ برای
دریافت
باقیمانده
تقسیم
متناظر
استفاده
میشود،
بهطوری که
‘x / y * y + x % y == x’
همیشه درست
باشد. حاصل
دارای همان
علامت
مقسومعلیه
است. این
باعث
میشود ‘x
% y’ برابر با
‘(x + y) % y’ یا
‘(x - y) % y’
باشد.
شیفت دادن با جابجایی تمام بیتها در عملوند سمت چپ به چپ (‘<<’) یا به راست (‘>>’) به میزان مقدار عملوند سمت راست انجام میشود. هنگام شیفت به چپ، تمام بیتهای تازه درجشده صفر میشوند؛ هنگام شیفت به راست، به جای آن کپیهایی از باارزشترین بیت اصلی هستند. این امر ‘a << b’ و ‘a >> b’ را به ترتیب معادل ضرب و تقسیم بر ۲ به توان b میکند.
عملگرهای مقایسه اگر مقایسه نادرست باشد ۰، و در غیر این صورت ۱ را برمیگردانند.
برخلاف
بسیاری از
زبانهای
دیگر، و به
دلایل فنی،
همچنان
هر دو
عملوند ‘&&’
و ‘||’ را
ارزیابی
میکند.
عملگرهای ‘&&’ و ‘&’ با یک ثابت صفر به عنوان هر یک از عملوندها، مقدار ثابت ۰ خواهند بود، و ‘||’ با یک ثابت غیرصفر به عنوان هر یک از عملوندها، مقدار ثابت ۱ خواهد بود، حتی اگر عملوند دیگر غیرثابت باشد.
‘!’ اگر عملوند ۰ باشد ۱ را برمیگرداند، و در غیر این صورت ۰ را برمیگرداند. حتی یک عملوند غیرثابت با هر بیت غیرصفر مقدار ۰ را برمیگرداند.
توابع اعداد صحیح (Integer functions)
علاوه بر عملگرها، توابعی نیز وجود دارند که کاربردهای تخصصیتری دارند:
| Name | Operation |
HIGH(n) |
معادل با
‘(n &
$FF00) >> 8’. |
LOW(n) |
معادل با
‘n &
$FF’. |
BITWIDTH(n) |
تعداد
بیتهای
لازم برای
نمایش n
را
برمیگرداند.
برخی
فرمولهای
مفید:
BITWIDTH(n) - 1
برابر با
است؛
BITWIDTH(n - 1)
برابر با
است؛ و
32 - BITWIDTH(n)
برابر با
، یعنی
تعداد
بیتهای
صفر آغازین
در نمایش
باینری n
است. |
TZCOUNT(n) |
مقدار ، یعنی تعداد بیتهای صفر پایانی در نمایش باینری n را برمیگرداند. |
عبارات ممیز ثابت (Fixed-point expressions)
اعداد
ممیز ثابت
از نظر فنی
همان اعداد
صحیح
هستند، اما
از نظر
مفهومی
دارای ممیز
اعشاری در
مکانی ثابت
هستند (به
همین دلیل
این نام را
دارند). این
امر به
آنها دقت
بیشتری
میدهد، به
بهای دامنه
کوچکتر،
در حالی که
دستکاری
آنها
بسیار
کمهزینهتر
از اعداد
ممیز شناور
باقی
میماند (که
از
آنها
پشتیبانی
نمیکند).
دقت
پیشفرض
تمام اعداد
ممیز ثابت
۱۶ بیت است،
به این معنی
که ۱۶ بیت
پایینی
برای بخش
کسری
استفاده
میشوند؛
بنابراین
آنها در
واحدهای یک
شصتوپنجهزاروپانصدوسیوششم
(65536ths) از ۱.۰
شمارش
میشوند.
این دقت را
میتوان با
گزینه خط
فرمان -Q
و/یا با OPT Q
تغییر داد
(به
Changing
options while assembling مراجعه
کنید). یک
لیترال
ممیز ثابت
مجزا
میتواند
دقت خود را
مشخص کند و
پیشفرض
جاری را لغو
نماید، با
افزودن یک
“q” که پس از
آن تعداد
بیتهای
کسری
میآید:
برای مثال،
‘789.25q8’
برابر با $000315_40 (
) است.
از آنجا که
مقادیر
ممیز ثابت
همچنان
همان اعداد
صحیح
هستند،
میتوانید
از آنها در
عبارات
عادی اعداد
صحیح
استفاده
کنید.
بهراحتی
میتوانید
یک عدد ممیز
ثابت را با
شیفت دادن
آن به راست
به تعداد
بیتهای
کسری، یا با
تقسیم آن بر
۱.۰، به یک
عدد صحیح
برش دهید (truncate).
نتیجه
میشود که
میتوانید
یک عدد صحیح
را با شیفت
دادن آن به
چپ به همان
میزان، یا
با ضرب کردن
آن در ۱.۰، به
یک عدد ممیز
ثابت تبدیل
کنید. برای
مثال، ‘123.0 /
1.0 == 123 ،’ و ‘123 *
1.0 == 123.0’.
توجه
داشته
باشید که
تعداد
بیتهای
کسری جاری
را میتوان
به صورت
TZCOUNT(1.0)
محاسبه
کرد.
توابع زیر برای کار با اعداد ممیز ثابت طراحی شدهاند (که باید ثابتِ مشخص باشند):
| Name | Operation |
DIV(x,
y) |
تقسیم ممیز ثابت |
MUL(x,
y) |
ضرب ممیز ثابت |
FMOD(x,
y) |
مدول ممیز ثابت |
POW(x,
y) |
|
LOG(x,
y) |
لگاریتم در مبنای |
ROUND(x) |
گرد کردن به نزدیکترین عدد صحیح (نیمه به دور از صفر) |
CEIL(x) |
گرد کردن به بالا به نزدیکترین عدد صحیح |
FLOOR(x) |
گرد کردن به پایین به نزدیکترین عدد صحیح |
SIN(x) |
سینوس |
COS(x) |
کسینوس |
TAN(x) |
تانژانت |
ASIN(x) |
وارون سینوس |
ACOS(x) |
وارون کسینوس |
ATAN(x) |
وارون تانژانت |
ATAN2(y,
x) |
زاویه بین و |
هیچ تابعی برای جمع و تفریق ممیز ثابت وجود ندارد، زیرا عملگرهای ‘+’ و ‘-’ میتوانند جفت عملوندهای ممیز ثابت را با هم جمع و تفریق کنند.
2.0 *
3’ معادل با
‘MUL(2.0, 3.0)’
است، و ‘6.0 /
2’ معادل با
‘DIV(6.0, 2.0)’
است. هنگام
انجام این
نوع کارها
مراقب
باشید و در
مورد معنای
عملیاتها
فکر کنید.همه این
توابع ممیز
ثابت
میتوانند
یک آرگومان
نهایی
اختیاری
بگیرند که
دقتی است که
باید برای
همان یک
عملیات
استفاده
شود. برای
مثال، ‘MUL(6.0q8,
7.0q8, 8)’
صرفنظر از
اینکه چه
مقداری به
عنوان
گزینه جاری
Q تنظیم
شده باشد،
به ‘42.0q8’
ارزیابی
میشود.
سازگاری
دقتها را
بررسی
نمیکند ،
بنابراین
ورودی
بیمعنی
مانند ‘MUL(4.2q8,
6.9q12, 16)’
نتیجهای
بیمعنی
(اما از نظر
فنی صحیح)
تولید
خواهد کرد:
“خروجی
بیارزش
حاصل ورودی
بیارزش
است (garbage in, garbage out)”.
تابع FMOD
برای به دست
آوردن
باقیمانده
تقسیم ممیز
ثابت
متناظر
استفاده
میشود.
نتیجه
دارای همان
علامت
مقسوم
است؛ این
برعکس نحوه
کار عملگر
مدول اعداد
صحیح ‘%’
است!
توابع
مثلثاتی
(SIN ، COS ،
TAN ، و
غیره) بر
حسب
دایرهای
تقسیمشده
به ۱.۰ “دور
(turn)” (برابر با
رادیان، یا
۳۶۰ درجه)
تعریف
شدهاند.
این توابع برای تولید خودکار جداول مختلف مفید هستند. برای مثال:
; Generate a table of 128 sine values
; from sin(0.0) included to sin(0.5) excluded,
; with amplitude scaled from [-1.0, 1.0] to [0.0, 128.0],
; then divided by 1.0 to round down to integer values.
FOR angle, 0.0, 0.5, 0.5 / 128
db MUL(SIN(angle) + 1.0, 128.0 / 2) / 1.0
ENDR
عبارات رشتهای (String expressions)
ابتداییترین
عبارت
رشتهای،
یک لیترال
رشتهای
است: هر
تعداد
نویسه که
درون گیومه
دوتایی
قرار گرفته
باشند (‘"for
instance"’). نویسه
بکاسلش
‘\’ خاص
است از این
جهت که باعث
میشود
نویسه بعد
از آن “گریز
داده شود (escaped)
،” به این
معنی که به
گونهای
متفاوت از
حالت معمول
با آن رفتار
میشود.
تعدادی
توالی گریز
وجود دارد
که
میتوانید
درون یک
رشته
استفاده
کنید:
| Sequence | Meaning |
‘\\’ |
بکاسلش (خود نویسه گریز را گریز میدهد) |
‘\"’ |
گیومه دوتایی (به رشته پایان نمیدهد) |
‘\'’ |
گیومه تکی (به لیترال نویسهای پایان نمیدهد) |
‘\{’ |
آکولاد باز (درج نماد را آغاز نمیکند) |
‘\}’ |
آکولاد بسته (به درج نماد پایان نمیدهد) |
‘\n’ |
خط جدید (ASCII $0A) |
‘\r’ |
بازگشت به ابتدای سطر (Carriage return) (ASCII $0D) |
‘\t’ |
تب (ASCII $09) |
‘\0’ |
نال (ASCII $00) |
لیترالهای
رشتهای
چندخطی در
گیومههای
سهتایی
قرار
میگیرند
(‘"""for
instance"""’).
توالیهای
گریز در
رشتههای
چندخطی به
همان صورت
کار
میکنند؛
با این حال،
نویسههای
خط جدید
تحتاللفظی
به همان شکل
گنجانده
میشوند،
بدون نیاز
به گریز
دادن آنها
با ‘\r’ یا
‘\n’.
لیترالهای
رشتهای
خام با یک
علامت هش ‘#’
پیشوندگذاری
میشوند.
درون
آنها،
بکاسلشها
و آکولادها
مانند
نویسههای
معمولی
تلقی
میشوند،
بنابراین
به عنوان
آرگومانهای
ماکرو،
نمادهای
درجشده یا
توالیهای
گریز بسط
داده
نخواهند شد.
برای مثال،
رشته خام
‘#"\t\1{s}\"’
معادل با
رشته
معمولی
‘"\\t\\1\{s}\\"’
است. (توجه
داشته
باشید که
این امر
مانع از
گنجاندن
نویسه
گیومه
دوتایی در
رشتههای
خام میشود.)
رشتههای
خام نیز
ممکن است
درون
گیومههای
سهتایی
قرار گیرند
تا چندخطی
باشند،
بنابراین
میتوانند
شامل
نویسههای
خط جدید یا
گیومه
تحتاللفظی
باشند
(هرچند
همچنان سه
گیومه
متوالی
مجاز نیست).
میتوانید
از عملگر ‘++’
برای الحاق
دو رشته
استفاده
کنید. ‘"str"
++ "ing"’ معادل
با ‘"string"
،’ یا
معادل با
‘STRCAT("str",
"ing")’ است.
میتوانید
از
عملگرهای
‘===’ و ‘!==’
برای
مقایسه دو
رشته
استفاده
کنید. ‘"str"
=== "ing"’ معادل
با ‘STRCMP("str",
"ing") == 0 ،’ و
‘"str" !== "ing"’
معادل با
‘STRCMP("str", "ing") !=
0’ است.
توابع زیر روی عبارات رشتهای عمل میکنند و خودشان رشته برمیگردانند:
| Name | Operation |
STRCAT(strs...) |
رشتههای strs را الحاق میکند. |
STRUPR(str) |
رشته str
را با
تمام حروف
اسکی
(‘a-z’) به
صورت حروف
بزرگ
برمیگرداند. |
STRLWR(str) |
رشته str
را با
تمام حروف
اسکی
(‘A-Z’) به
صورت حروف
کوچک
برمیگرداند. |
STRSLICE(str,
start, stop) |
یک زیررشته از str را که از start آغاز شده و به stop ختم میشود (غیرشامل) برمیگرداند. اگر stop مشخص نشده باشد، زیررشته تا انتهای str ادامه مییابد. |
STRRPL(str,
old, new) |
رشته str را با جایگزینی هر رخداد زیررشته old با new برمیگرداند. |
STRFMT(fmt,
args...) |
رشته fmt
را
بازمیگرداند
در حالی که
هر الگوی
‘%spec’ با
درج قالب
spec (با
استفاده از
همان
ساختار
دستوری
Symbol interpolation)
با آرگومان
متناظر آن
در args
جایگزین
شده است (‘%%’
با نویسه
‘%’ جایگزین
میشود). |
STRCHAR(str,
idx) |
زیررشتهای از str را برای ورودی جدول نگاشت نویسه در اندیس idx با نگاشت نویسه جاری برمیگرداند. (idx ورودیهای نگاشت نویسه را میشمارد، نه نویسهها را.) |
توابع زیر عملوندهای مختلفی میگیرند و رشته برمیگردانند:
REVCHAR(vals...) |
رشتهای را که با نگاشت نویسه جاری به vals نگاشت شده برمیگرداند. اگر ورودی نگاشت نویسه یکتایی برای vals وجود نداشته باشد، خطا رخ میدهد. |
READFILE(name,
max) |
محتوای
فایل name
را به
صورت رشته
برمیگرداند.
حداکثر تا
max
بایت را
میخواند،
یا اگر max
مشخص
نشده باشد،
کل محتوا را
میخواند.
اگر فایل در
دایرکتوری
جاری یافت
نشود،
فهرست
مسیرهای include
ارائهشده
به گزینه
-I
-دستور
rgbasm(1) در
خط فرمان
جستجو
خواهد شد. |
توابع زیر روی عبارات رشتهای عمل میکنند، اما اعداد صحیح برمیگردانند:
| Name | Operation |
STRLEN(str) |
تعداد نویسهها در str را برمیگرداند. |
STRCMP(str1,
str2) |
رشتههای str1 و str2 را بر اساس ترتیب اسکی نویسههایشان مقایسه میکند. اگر str1 کمتر از str2 باشد -1، اگر str1 بزرگتر از str2 باشد 1، و اگر با هم مطابقت داشته باشند 0 برمیگرداند. |
STRFIND(str,
sub) |
اولین اندیس رخداد sub در str را برمیگرداند، یا اگر موجود نباشد -1 برمیگرداند. |
STRRFIND(str,
sub) |
آخرین اندیس رخداد sub در str را برمیگرداند، یا اگر موجود نباشد -1 برمیگرداند. |
BYTELEN(str) |
تعداد بایتها در str را برمیگرداند. (نویسههای غیراسکی میتوانند چندین بایت باشند.) |
STRBYTE(str,
idx) |
مقدار بایت را در اندیس idx در str برمیگرداند. |
INCHARMAP(str) |
اگر str ورودیای در نگاشت نویسه جاری داشته باشد 1، و در غیر این صورت 0 برمیگرداند. |
CHARLEN(str) |
تعداد ورودیهای نگاشت نویسه در str را با نگاشت نویسه جاری برمیگرداند. |
CHARCMP(str1,
str2) |
رشتههای str1 و str2 را بر اساس مقادیر ورودیهای نگاشت نویسه آنها با نگاشت نویسه جاری مقایسه میکند. اگر str1 کمتر از str2 باشد -1، اگر str1 بزرگتر از str2 باشد 1، و اگر با هم مطابقت داشته باشند 0 برمیگرداند. |
CHARSIZE(char) |
تعداد مقادیر موجود در ورودی نگاشت نویسه برای char را با نگاشت نویسه جاری برمیگرداند. |
CHARVAL(char,
idx) |
مقدار موجود در اندیس idx از ورودی نگاشت نویسه برای char را برمیگرداند. اگر idx مشخص نشده باشد، char باید دارای یک مقدار واحد باشد که همان برگردانده میشود. |
توجه
داشته
باشید که
اندیسها
از ابتدا با
۰، یا از
انتها با ۱-
شمارش
میشوند.
نویسههای
یک رشته
توسط ‘STRLEN
؛’
ورودیهای
نگاشت
نویسه یک
رشته توسط
‘CHARLEN ؛’ و
مقادیر یک
ورودی
نگاشت
نویسه توسط
‘CHARSIZE’
شمارش
میشوند.
نگاشتهای نویسه (Character maps)
هنگام نوشتن رشتههای متنی که قرار است روی Game Boy نمایش داده شوند، ممکن است رمزگذاری نویسه در ROM با رمزگذاری فایل منبع تفاوت داشته باشد. برای مثال، تایلهای مورد استفاده برای حروف بزرگ ممکن است با شروع از اندیس تایل ۱۲۸ قرار گیرند، که با اسکی که از ۶۵ آغاز میشود متفاوت است.
نگاشتهای نویسه امکان نگاشت رشتهها یا لیترالهای نویسهای را به توالیهای دلخواهی از اعداد فراهم میکنند:
CHARMAP "A", 42 CHARMAP ':)', 39 CHARMAP "<br>", 13, 10 CHARMAP '€', $20ac
این منجر
به این
میشود که
‘db "Amen :)<br>"’
معادل با
‘db 42, 109, 101, 110, 32, 39, 13, 10
،’ و ‘dw
"25€"’
معادل با
‘dw 50, 53, $20ac’
باشد.
نگاشتهای نویسه به صورت حریصانه (greedily) تطبیق داده میشوند، بنابراین طولانیترین نگاشت قابل اعمال در یک رشته نگاشت خواهد شد. هر نویسهای در رشته که بدون نگاشت تعریفشده باشد، مستقیماً و با استفاده از رمزگذاری نویسهها به بایتهای فایل منبع کپی خواهد شد.
امکان ایجاد چندین نگاشت نویسه و سپس جابجایی بین آنها بر حسب نیاز وجود دارد. برای مثال، میتوان از این ویژگی برای رمزگذاری اطلاعات اشکالزدایی در اسکی و استفاده از رمزگذاری متفاوت برای اهداف دیگر استفاده کرد. در ابتدا، یک نگاشت نویسه به نام ‘main’ وجود دارد و از آغاز بهطور خودکار به عنوان نگاشت نویسه جاری انتخاب میشود. همچنین یک پشته نگاشت نویسه وجود دارد که میتوان از آن برای ذخیره و بازیابی نگاشت نویسه فعال جاری استفاده کرد.
| Command | Meaning |
NEWCHARMAP
name |
یک نگاشت نویسه جدید و خالی به نام name ایجاد کرده و به آن سوئیچ میکند. |
NEWCHARMAP
name, basename |
یک نگاشت نویسه جدید به نام name ایجاد میکند که از نگاشت نویسه basename کپی شده است، و به آن سوئیچ میکند. |
SETCHARMAP
name |
به نگاشت نویسه name سوئیچ میکند. |
PUSHC |
نگاشت نویسه جاری را روی پشته قرار میدهد (push). |
PUSHC
name |
نگاشت نویسه جاری را روی پشته قرار داده و به نگاشت نویسه name سوئیچ میکند. |
POPC |
یک نگاشت نویسه را از پشته برمیدارد (pop) و به آن سوئیچ میکند. |
نکته: تغییرات اعمالشده روی یک نگاشت نویسه بلافاصله از همان نقطه به بعد اثرگذار خواهند بود.
سایر توابع (Other functions)
چند تابع دیگر وجود دارند که کارهایی فراتر از عملیات عددی یا رشتهای انجام میدهند:
| Name | Operation |
DEF(symbol) |
اگر symbol تعریف شده باشد ۱، و در غیر این صورت ۰ برمیگرداند. ثابتهای رشتهای درون پرانتزها بسط داده نمیشوند. |
ISCONST(arg) |
اگر مقدار
arg توسط RGBASM
شناختهشده
باشد ۱
(مثلاً اگر
بتواند
آرگومانی
برای IF
باشد)، یا
اگر فقط RGBLINK
بتواند
مقدار آن را
محاسبه کند
۰
برمیگرداند. |
BANK(arg) |
یک شماره
بانک را
برمیگرداند.
اگر arg
نماد @
باشد، این
تابع بانکِ
بخش جاری را
برمیگرداند.
اگر arg یک
رشته باشد،
بانک بخشی
را که دارای
آن نام است
برمیگرداند.
اگر arg یک
برچسب
باشد،
شماره
بانکی را که
برچسب در آن
قرار دارد
برمیگرداند.
اگر
قادر به
محاسبه آن
باشد، حاصل
ممکن است
ثابت باشد. |
SECTION(symbol) |
نام بخشی
را که symbol
در آن تعریف
شده است
برمیگرداند.
symbol باید
پیشتر
تعریف شده
باشد. اگر
arg نماد
@ باشد،
این تابع
نام بخش
جاری را
برمیگرداند. |
SIZEOF(arg) |
اگر arg یک رشته باشد، این تابع اندازه بخش با نام arg را برمیگرداند. اگر arg یک کلیدواژه نوع بخش باشد، اندازه آن نوع بخش را برمیگرداند. حاصل ثابت نیست، زیرا تنها RGBLINK میتواند مقدار آن را محاسبه کند. اگر arg یک ثبات ۸ بیتی یا ۱۶ بیتی باشد، اندازه آن ثبات را برمیگرداند. |
STARTOF(arg) |
اگر arg یک رشته باشد، این تابع آدرس شروع بخش با نام arg را برمیگرداند. اگر arg یک کلیدواژه نوع بخش باشد، آدرس شروع آن نوع بخش را برمیگرداند. حاصل ثابت نیست، زیرا تنها RGBLINK میتواند مقدار آن را محاسبه کند. |
بخشها (SECTIONS)
پیش از آنکه بتوانید شروع به نوشتن کد کنید، باید یک بخش تعریف کنید. این کار به اسمبلر میگوید چه نوع اطلاعاتی در ادامه میآید و کجا باید قرار گیرد.
SECTION name,
typeSECTION name,
type, optionsSECTION name,
type[addr]SECTION name,
type[addr],
optionsname رشتهای محصور در گیومههای دوتایی است که نام بخش میباشد. اگر نوع تطابق نداشته باشد، خطا رخ میدهد. هر بخش باید نامی یکتا داشته باشد، حتی در میان فایلهای منبع مختلف، در غیر این صورت پیونددهنده آن را به عنوان خطا در نظر میگیرد.
انواع type ممکن برای بخش به شرح زیر هستند:
ROM0- یک بخش ROM. addr میتواند از $0000 تا $3FFF ، یا از $0000 تا $7FFF در صورت فعال بودن حالت tiny ROM در پیونددهنده باشد.
ROMX- یک بخش ROM
بانکبندیشده
(banked). addr
میتواند
از $4000 تا
$7FFF باشد.
در صورت
فعال بودن
حالت tiny ROM در
پیونددهنده،
به نام
مستعاری
برای
ROM0تبدیل میشود. VRAM- یک بخش رم ویدیویی (VRAM) بانکبندیشده. addr میتواند از $8000 تا $9FFF باشد. bank میتواند ۰ یا ۱ باشد، اما اگر حالت DMG در پیونددهنده فعال باشد، بانک ۱ در دسترس نیست.
SRAM- یک بخش رم خارجی (ذخیرهسازی) بانکبندیشده. addr میتواند از $A000 تا $BFFF باشد.
WRAM0- یک بخش رم عمومی (general-purpose RAM). addr میتواند از $C000 تا $CFFF ، یا از $C000 تا $DFFF در صورت فعال بودن حالت WRAM0 در پیونددهنده باشد.
WRAMX- یک بخش رم
عمومی
بانکبندیشده.
addr
میتواند
از $D000 تا
$DFFF باشد.
bank
میتواند
از ۱ تا ۷
باشد. در
صورت فعال
بودن حالت WRAM0
در
پیونددهنده،
به نام
مستعاری
برای
WRAM0تبدیل میشود. OAM- یک بخش رم ویژگی اشیاء (OAM). addr میتواند از $FE00 تا $FE9F باشد.
HRAM- یک بخش رم بالا (High RAM). addr میتواند از $FF80 تا $FFFE باشد.
مجموعه RGBDS
رامها (ROMs) را
تولید
میکند، به
این معنی که
کد و داده
فقط
میتوانند
در بخشهای
ROM0 و ROMX
قرار گیرند.
سایر انواع
بخشهای RAM
برای
برچسبهایی
با تخصیص
ایستا
هستند. اگر
به کد یا
داده در RAM
نیاز
دارید،
باید
خودتان آن
را از ROM به RAM
کپی کنید.
برای
نمونهای
از چگونگی
انجام راحت
این کار با
یک بلوک LOAD
، به RAM code
مراجعه
کنید.
گزینههای option با کاما جدا میشوند و ممکن است شامل موارد زیر باشند:
BANK[bank]- مشخص میکند که پیونددهنده بخش را در کدام bank قرار دهد. برای مقادیر ممکن bank بسته به type ، به بالا مراجعه کنید.
ALIGN[align, offset]- بخش را در
آدرسی قرار
میدهد که
align بیت
کمارزش آن
برابر با
offset باشند.
توجه داشته
باشید که
ALIGN[align] کوتاهنوشتی برایALIGN[align, 0] است. این گزینه میتواند همراه با [addr] استفاده شود، تا زمانی که با یکدیگر تناقض نداشته باشند. همچنین میتوان در میانه یک بخش تراز درخواست کرد؛ به Requesting alignment در ادامه مراجعه کنید.
اگر [addr]
مشخص نشده
باشد، بخش
“شناور (floating)”
در نظر
گرفته
میشود؛
پیونددهنده
بهطور
خودکار
آدرس
مناسبی را
برای بخش
محاسبه
خواهد کرد.
به همین
ترتیب، اگر
BANK[bank]
مشخص نشده
باشد،
پیونددهنده
بهطور
خودکار
بانکی با
فضای کافی
پیدا خواهد
کرد.
بخشها را میتوان با استفاده از یک فایل اسکریپت پیونددهنده نیز مکانیابی کرد. قالب آن در rgblink(5) توضیح داده شده است. این اسکریپتها به کاربر اجازه میدهند بخشهای شناور را در بانک مورد نظر و به ترتیبی که در اسکریپت مشخص شده قرار دهد. این امر زمانی مفید است که نتوان بخشها را بهصورت دستی در یک آدرس قرار داد زیرا اندازه ممکن است تغییر کند، اما بخشها باید در کنار هم باشند.
مثالهای بخش:
-
SECTION "Cool Stuff", ROMX
این دستور به بخش با نام “Cool Stuff” سوئیچ میکند و اگر از قبل وجود نداشته باشد، آن را ایجاد مینماید. این بخش میتواند در هر بانک ROM قرار گیرد. کد و داده میتوانند پس از آن بیایند.
- در صورت
نیاز،
میتوان
آدرس پایه
بخش را مشخص
کرد:
SECTION "Cool Stuff", ROMX[$4567]
- مثالی با
بانک ثابت:
SECTION "Cool Stuff", ROMX[$4567], BANK[3]
- و اگر
بخواهید
تنها بانک
بخش را
اجبار کنید
و نه موقعیت
آن در بانک
را، این نیز
ممکن است:
SECTION "Cool Stuff", ROMX, BANK[7]
- مثالهای
ترازسازی:
مورد اول
میتواند
برای تعریف
یک بافر OAM که
قرار است با
DMA منتقل شود
مفید باشد،
زیرا باید
در مرز ۲۵۶
بایت تراز
شود. مورد
دوم نیز
میتواند
برای GBC HDMA، یا
برای یک کد
کپی
بهینهشده
که نیازمند
ترازسازی
است مناسب
باشد.
SECTION "OAM Data", WRAM0, ALIGN[8] ; align to 256 bytes SECTION "VRAM Data", ROMX, BANK[2], ALIGN[4] ; align to 16 bytes
بخش جاری
را میتوان
بدون شروع
یک بخش جدید
با استفاده
از ENDSECTION
پایان داد.
این
شبهدستور
زمینه بخش
را پاک
میکند،
بنابراین
تا زمانی که
بخش دیگری
را شروع
نکنید دیگر
نمیتوانید
کد بنویسید.
این
میتواند
برای
جلوگیری از
تعریف
تصادفی کد
یا داده در
بخش اشتباه
مفید باشد.
پشته بخش (Section stack)
POPS و
PUSHS رابط
کار با پشته
بخش را
فراهم
میکنند.
تعداد
ورودیها
در پشته
تنها به
میزان
حافظه
موجود در
سیستم شما
محدود
میشود.
PUSHS
زمینه بخش
جاری را روی
پشته بخش
قرار
میدهد (push).
سپس
میتوان از
POPS بعداً
برای
بازیابی آن
استفاده
کرد. این
ویژگی برای
تعریف
بخشها در
فایلهای include
مفید است
وقتی
نمیخواهید
زمینه بخش
در نقطهای
که فایل include
شده
بازنویسی
شود.
PUSHS
همچنین
میتواند
همان
آرگومانهای
SECTION را
بگیرد تا
زمینه بخش
جاری را در
پشته قرار
داده و
همزمان یک
بخش جدید
تعریف کند:
SECTION "Code", ROM0
Function:
ld a, 42
PUSHS "Variables", WRAM0
wAnswer: db
POPS
ld [wAnswer], a
کد رم (RAM code)
گاهی
اوقات
میخواهید
کدی (یا
دادهای) در
RAM داشته
باشید،
مثلاً برای
کد
خودتغییردهنده
(self-modifying code). اما
نمیتوانید
آن را
مستقیماً
در یک بخش RAM
قرار دهید؛
بلکه باید
آن را در ROM
ذخیره کرده
و در
نقطهای از
زمان به RAM
کپی کنید.
این بدان
معنی است که
کد در
محدوده
آدرسی
متفاوت از
جایی که
تعریف شده
اجرا خواهد
شد، که این
میتواند
برای ارجاع
به
برچسبهای
درون آن کد
ناخوشایند
باشد. این
وضعیت
دقیقاً
همان چیزی
است که
بلوکهای
LOAD برای
آن طراحی
شدهاند. در
اینجا
مثالی از
نحوه
استفاده از
آنها آمده
است:
SECTION "LOAD example", ROMX
CopyCode:
ld de, RAMCode
ld hl, RAMLocation
ld c, RAMCode.end - RAMCode
.loop
ld a, [de]
inc de
ld [hli], a
dec c
jr nz, .loop
ret
RAMCode:
LOAD "RAM code", WRAM0
RAMLocation:
ld hl, .string
ld de, $9864
.copy
ld a, [hli]
ld [de], a
inc de
and a
jr nz, .copy
ret
.string
db "Hello World!\0"
ENDL
.end
یک بلوک
LOAD شبیه
به اعلان
SECTION به نظر
میرسد
زیرا یک بخش
جدید ایجاد
میکند.
تمام
دادهها و
کدهای
تولیدشده
در چنین
بلوکی
مانند
معمول در
بخش جاری
قرار
میگیرند،
اما تمام
برچسبها
طوری ایجاد
میشوند که
گویی در این
بخش
تازهتأسیس
قرار
گرفتهاند.
در مثال بالا، تمام کدها و دادهها در بخش “LOAD example” قرار خواهند گرفت. متوجه برچسبهای ‘RAMCode’ و ‘RAMLocation’ خواهید شد. اولی در ROM واقع شده است، جایی که کد ذخیره میشود؛ دومی در RAM قرار دارد، جایی که کد بارگذاری خواهد شد.
نمیتوانید
بلوکهای
LOAD را
تودرتو
کنید، و
همچنین
نمیتوانید
بخش جاری را
در درون
آنها
تغییر دهید
یا متوقف
سازید.
بلوک LOAD
جاری را
میتوان با
استفاده از
ENDL پایان
داد. این
شبهدستور
تنها زمانی
لازم است که
بخواهید
نوشتن کد را
در بخش ROM
دربرگیرنده
آن از سر
بگیرید. هر
یک از
دستورهای
LOAD ، SECTION ، ENDSECTION ،
یا POPS پیش
از انجام
عملکرد
خود، بلوک
LOAD جاری
را پایان
خواهند
داد.
بلوکهای
LOAD
میتوانند
از
اصلاحکنندههای
UNION یا
FRAGMENT
همانطور
که در بخش
Unionized sections
در ادامه
شرح داده
شده
استفاده
کنند.
بخشهای مشترک (Unionized sections)
هنگامی که
با کمبود RAM
مواجه
هستید،
ممکن است
بخواهید
تخصیصهای
حافظه
ایستای
همپوشان
تعریف
کنید،
همانطور
که در بخش
Allocating
overlapping spaces in RAM توضیح
داده شده
است. با این
حال، یک
UNION تنها
درون یک
فایل منفرد
کار
میکند،
بنابراین
نمیتواند
مثلاً برای
تعریف
متغیرهای
موقت در
چندین فایل
استفاده
شود که همگی
از یک حافظه
تخصیصیافته
ایستای
مشترک
استفاده
میکنند.
بخشهای
مشترک (Unionized sections)
این مشکل را
برطرف
میکنند.
برای اعلان
یک بخش
مشترک،
کلیدواژه
UNION را بعد
از
کلیدواژه
SECTION اضافه
کنید؛
اعلان در
سایر موارد
تفاوتی
ندارد.
بخشهای
مشترک از
چند قانون
متفاوت
نسبت به
بخشهای
معمولی
پیروی
میکنند:
- همان بخش
مشترک (یعنی
دارای نام
یکسان) را
میتوان
چندین بار
در هر
فراخوانی
و در چندین فراخوانی اعلام کرد. با اعلانهای مختلف به صورت یکسان رفتار شده و ادغام میشوند، چه در یک فراخوانی باشند یا فراخوانیهای متفاوت. - اگر یک بخش به عنوان مشترک اعلان شده باشد، تمام بخشهای با همان نام نیز باید به صورت مشترک اعلان شوند.
- تمام
اعلانها
باید نوع
یکسانی
داشته
باشند. برای
مثال، حتی
اگر فلگ
-wدستور rgblink(1) استفاده شده باشد، انواعWRAM0وWRAMXهمچنان متفاوت در نظر گرفته میشوند. - میتوان قیدهای متفاوتی (ترازسازی، بانک و غیره) برای هر اعلان بخش مشترک تعیین کرد، اما همه آنها باید سازگار باشند. برای مثال، ترازسازی باید با هر آدرس ثابتی سازگار باشد، تمام بانکهای مشخصشده باید یکسان باشند، و غیره.
- بخشهای
مشترک
نمیتوانند
دارای نوع
ROM0یاROMXباشند.
اعلانهای مختلف از یک بخش مشترک یکسان به یکدیگر ضمیمه نمیشوند، بلکه درست مانند Allocating overlapping spaces in RAM بر روی یکدیگر منطبق و همپوشان میشوند. به همین ترتیب، اندازه یک بخش مشترک برابر با بزرگترین اندازه در میان تمام اعلانهای آن است.
قطعات بخش (Section fragments)
قطعات بخش
(Section fragments)،
بخشهایی
با یک تغییر
کوچک هستند:
هنگامی که
چندین قطعه
با یک نام
یافت
میشوند،
به جای
ایجاد خطا،
در قالب یک
بخش به هم
الحاق
میشوند،
حتی در
چندین فایل
شیء. این امر
درون یک
فایل کار
میکند
(همانند
رفتاری که
بخشهای
«ساده» در
نسخههای
قبلی
داشتند)،
اما در میان
فایلهای
شیء مختلف
نیز کار
میکند.
برای اعلان
یک قطعه
بخش،
کلیدواژه
FRAGMENT را بعد
از
کلیدواژه
SECTION اضافه
کنید؛
اعلان در
سایر موارد
تفاوتی
ندارد. با
این حال،
مشابه با
Unionized sections
، باید
برخی
قوانین
رعایت
شوند:
- اگر یک بخش به عنوان قطعه اعلان شده باشد، تمام بخشهای با همان نام نیز باید به صورت قطعه اعلان شوند.
- تمام
اعلانها
باید دارای
نوع یکسانی
باشند. برای
مثال، حتی
اگر فلگ
-wدستور rgblink(1) استفاده شده باشد، انواعWRAM0وWRAMXهمچنان متفاوت در نظر گرفته میشوند. - میتوان قیدهای متفاوتی (ترازسازی، بانک و غیره) برای هر اعلان قطعه بخش تعیین کرد، اما همه آنها باید سازگار باشند. برای مثال، ترازسازی باید با هر آدرس ثابتی سازگار باشد، تمام بانکهای مشخصشده باید یکسان باشند، و غیره.
- یک قطعه بخش نمیتواند مشترک (unionized) باشد؛ زیرا در نهایت، چنین چیزی چندان منطقی نخواهد بود.
هنگامی که RGBASM دو قطعه را ادغام میکند، قطعهای که دیرتر یافت شده به قطعهای که زودتر یافت شده ضمیمه میشود.
هنگامی که
RGBLINK دو قطعه را
ادغام
میکند،
قطعهای که
فایل آن در
انتها مشخص
شده، به
قطعهای که
فایل آن در
ابتدا مشخص
شده ضمیمه
میشود.
برای مثال،
با فرض
اینکه ‘bar.o
،’ ‘baz.o
،’ و
‘foo.o’
همگی حاوی
قطعهای با
همان نام
باشند،
دستور
rgblink -o rom.gb baz.o foo.o
bar.obaz.o ،’
سپس قطعه
حاصل از
‘foo.o ،’ و
در پایان
قطعه حاصل
از ‘bar.o’
را تولید
خواهد کرد.
لیترالهای قطعه (Fragment literals)
لیترالهای
قطعه (Fragment literals)
برای
بلوکهای
کوتاهی از
کد یا داده
که تنها یک
بار به
آنها
ارجاع داده
میشود
مفید هستند.
آنها
قطعات بخشی
هستند که با
محصور کردن
دستورالعملها
یا
شبهدستورها
با
براکتهای
دوتایی
‘[[’ و
‘]]’
ایجاد
میشوند،
بدون نیاز
به اعلان
جداگانه
SECTION FRAGMENT.
محتوای یک
لیترال
قطعه تبدیل
به یک SECTION FRAGMENT
میشود، که
نام و بانک
یکسانی با
بخش ROM والد
خود به
اشتراک
میگذارد،
اما بدون
هیچ قید
دیگری.
همچنین بخش
والد اگر از
قبل یک قطعه
نبوده
باشد، به یک
FRAGMENT تبدیل
میشود، تا
بتواند با
لیترالهای
قطعه خود
ادغام گردد.
پیونددهنده
RGBLINK قطعات را
بدون ترتیب
خاصی ادغام
میکند.
یک لیترال
قطعه
میتواند
جایگزین هر
ثابت عدد
صحیح ۱۶
بیتی ‘n16’
از مستندات
gbz80(7) و
همچنین یک
مورد DW
شود. سپس
لیترال
قطعه به
آدرس شروع
خود
ارزیابی
میشود.
برای مثال،
میتوانید
به یک
لیترال
قطعه CALL
یا JP
بزنید.
این کد با استفاده از برچسبهای نامگذاریشده:
DataTable:
dw First
dw Second
dw Third
First: db 1
Second: db 4
Third: db 9
Routine:
push hl
ld hl, Left
jr z, .got_it
ld hl, Right
.got_it
call .print
pop hl
ret
.print:
ld de, $1003
ld bc, STARTOF(VRAM)
jp Print
Left: db "left\0"
Right: db "right\0"
معادل با این کد با استفاده از لیترالهای قطعه است:
DataTable:
dw [[ db 1 ]]
dw [[ db 4 ]]
dw [[ db 9 ]]
Routine:
push hl
ld hl, [[ db "left\0" ]]
jr z, .got_it
ld hl, [[ db "right\0" ]]
.got_it
call [[
ld de, $1003
ld bc, STARTOF(VRAM)
jp Print
]]
pop hl
ret
تفاوت در این است که مثال استفادهکننده از لیترالهای قطعه، ترتیب خاصی را برای اجزای خود اعلان نمیکند.
لیترالهای قطعه میتوانند به دلخواه تودرتو شوند، بنابراین موارد استفاده افراطی نیز از نظر فنی امکانپذیر است. این کد با استفاده از برچسبهای نامگذاریشده:
dw FortyTwo
FortyTwo:
call Sub1
jr Sub2
Sub1:
ld a, [Twenty]
ret
Twenty: db 20
Sub2:
jp Sub3
Sub3:
call Sub1
inc a
add a
ret
معادل با این کد با استفاده از لیترالهای قطعه است:
dw [[
call [[
Sub1: ld a, [ [[db 20]] ] :: ret
]]
jr [[
jp [[ call Sub1 :: inc a :: add a :: ret ]]
]]
]]
نمادها (SYMBOLS)
پروژه RGBDS از چندین نوع نماد پشتیبانی میکند:
- برچسب (Label)
- نماد عددی که یک مکان حافظه را مشخص میکند. ممکن است مقداری شناختهشده در زمان اسمبل داشته باشد یا نداشته باشد.
- ثابت (Constant)
- نماد عددی که مقدار آن باید در زمان اسمبل شناختهشده باشد.
- ماکرو (Macro)
- بلوکی از کد
که بعداً میتواند فراخوانی شود. - رشته (String)
- یک رشته متنی که بعداً میتواند مشابه یک ماکرو بسط داده شود.
نامهای
نمادها
میتوانند
شامل حروف
اسکی،
اعداد، خط
زیر ‘_ ،’
علامت هش ‘#
،’ علامت
دلار ‘$ ،’ و
علامت
اتساین ‘@’
باشند. با
این حال،
آنها باید
با یک حرف یا
یک خط زیر
آغاز شوند.
علاوه بر
این،
نامهای
برچسب
میتوانند
حداکثر
شامل یک
نقطه ‘.’
باشند که
نباید
نویسه اول
باشد.
یک نماد
نمیتواند
همنام با یک
کلمه کلیدی
رزرو شده
باشد، مگر
اینکه نام
آن یک
“شناسه
خام” با
پیشوند هش
‘#’ باشد. به
عنوان
مثال،
‘#load’
نشاندهنده
نمادی به
نام ‘load’
است، و
‘#LOAD’
نشاندهنده
نماد
متفاوتی به
نام ‘LOAD’
است؛ در هر
دو مورد، ‘#’
از تلقی شدن
آنها به
عنوان کلمه
کلیدی LOAD
جلوگیری
میکند.
برچسبها (Labels)
یکی از وظایف اصلی اسمبلر، پیگیری آدرسها برای شماست، تا بتوانید به جای اعداد “جادویی” با نامهای با معنی کار کنید. برچسبها دقیقاً این امکان را فراهم میکنند: یک برچسب یک نام را به یک مکان خاص درون یک بخش پیوند میدهد. یک برچسب به یک بانک و آدرس ترجمه میشود که همزمان با بخش والد آن تعیین میگردد (ادامه این بخش را ببینید).
یک برچسب
با نوشتن
نام آن در
ابتدای یک
خط، به
همراه یک یا
دو دونقطه
تعریف
میشود،
بدون هیچ
فاصله خالی
بین نام
برچسب و
دونقطه(ها).
تعریف یک
برچسب
(سراسری یا
محلی) با دو
دونقطه
‘::’ آن را
همزمان
تعریف و
صادر (EXPORT)
میکند. (به
صادر
و وارد کردن
نمادها (Exporting and importing
symbols) در زیر
مراجعه
کنید). هنگام
تعریف یک
برچسب
محلی،
دونقطه
میتواند
حذف شود و
طوری
رفتار
خواهد کرد
که گویی
تنها یک
دونقطه
وجود داشته
است.
یک برچسب
در صورتی
محلی
نامیده
میشود که
نام آن شامل
یک نقطه
‘.’
باشد؛ در
غیر این
صورت،
سراسری
نامیده
میشود (با
“صادر شده
،” که در
صادر
و وارد کردن
نمادها (Exporting and importing
symbols) در زیر
توضیح داده
شده،
اشتباه
گرفته نشود).
وجود بیش از
یک نقطه در
نامهای
برچسب مجاز
نیست.
برای سهولت، برچسبهای محلی میتوانند از نحو خلاصه استفاده کنند: زمانی که نام نمادی که با نقطه شروع میشود یافت شود (به عنوان مثال، درون یک عبارت یا هنگام تعریف یک برچسب)، “حوزه برچسب” فعلی به طور ضمنی به ابتدای آن افزوده میشود.
تعریف یک برچسب سراسری، آن را به عنوان “حوزه برچسب” فعلی تنظیم میکند، تا زمانی که تعریف برچسب سراسری بعدی، یا پایان بخش فعلی فرا برسد.
در اینجا چند مثال از تعاریف برچسب آورده شده است:
GlobalLabel: AnotherGlobal: .locallabel ; This defines "AnotherGlobal.locallabel" .another_local: AnotherGlobal.with_another_local: ThisWillBeExported:: ; Note the two colons ThisWillBeExported.too::
در یک
عبارت
عددی، یک
برچسب به
آدرس خود در
حافظه
ارزیابی
میشود.
(برای به دست
آوردن بانک
آن، از تابع
‘BANK()’
توصیف شده
در Other functions
استفاده
کنید). به
عنوان
مثال، با
توجه به
موارد زیر،
‘ld de, vPlayerTiles’
معادل ‘ld de,
$80C0’ خواهد
بود با فرض
اینکه بخش
در $80C0
قرار گیرد:
SECTION "Player tiles", VRAM
vPlayerTiles:
ds 6 * 16
.end
مکان یک برچسب (و در نتیجه مقدار آن) معمولاً تا مرحله پیوند تعیین نمیشود، بنابراین برچسبها معمولاً نمیتوانند به عنوان ثابتها استفاده شوند. با این حال، اگر بخشی که برچسب در آن تعریف شده دارای یک آدرس پایه ثابت باشد، مقدار آن در زمان اسمبل مشخص است.
همچنین،
در حالی که
بدیهی
است
میتواند
تفاضل بین
دو برچسب را
در صورت
ثابت بودن
هر دو
محاسبه
کند، قادر
است تفاضل
بین دو
برچسب
غیرثابت را
نیز در
صورتی که هر
دو به یک بخش
تعلق داشته
باشند،
مانند
‘PlayerTiles’ و
‘PlayerTiles.end’ در
بالا،
محاسبه
کند.
برچسبهای ناشناس (Anonymous labels)
برچسبهای ناشناس برای بلوکهای کوتاه کد مفید هستند. آنها مانند برچسبهای معمولی تعریف میشوند، اما بدون نام قبل از دونقطه. برچسبهای ناشناس مستقل از حوزه برچسب هستند، بنابراین تعریف یکی از آنها برچسب حوزه را تغییر نمیدهد، و ارجاع به آن تحت تأثیر برچسب حوزه فعلی قرار نمیگیرد.
برچسبهای
ناشناس با
استفاده از
یک دونقطه
‘:’ به
همراه
علامتهای
مثبت ‘+’
یا منفی
‘-’
ارجاع داده
میشوند.
بنابراین
:+ به
برچسب بعدی
پس از
عبارت، :++
به برچسب
بعد از آن؛
:- به
برچسب قبلی
قبل از
عبارت
ارجاع
میدهد؛ و
به همین
ترتیب.
ld hl, :++
: ld a, [hli] ; referenced by "jr nz"
ldh [c], a
dec c
jr nz, :-
ret
: ; referenced by "ld hl"
dw $7FFF, $1061, $03E0, $58A5
متغیرها (Variables)
یک علامت مساوی ‘=’ برای تعریف نمادهای عددی تغییرپذیر استفاده میشود. برخلاف سایر نمادهای توضیح داده شده در زیر، متغیرها میتوانند دوباره تعریف شوند. این قابلیت برای نمادهای داخلی در ماکروها، شمارندهها و غیره مفید است.
DEF ARRAY_SIZE EQU 4 DEF COUNT = 2 DEF COUNT = 3 DEF COUNT = ARRAY_SIZE + COUNT DEF COUNT *= 2 ; COUNT now has the value 14
توجه
داشته
باشید که
قرار دادن
دونقطه
‘:’ پس از
نام مجاز
نیست.
متغیرها را میتوان به راحتی با عملگرهای انتساب ترکیبی مانند C دوباره تعریف کرد:
| عملگر | معنی |
+= -= |
جمع/تفریق ترکیبی |
*= /=
%= |
ضرب/تقسیم/باقیمانده ترکیبی |
<<=
>>= |
شیفت به چپ/راست ترکیبی |
&= =
^= |
عملیات and/or/xor ترکیبی |
مثالها:
DEF x = 10 DEF x += 1 ; x == 11 DEF y = x - 1 ; y == 10 DEF y *= 2 ; y == 20 DEF y >>= 1 ; y == 10 DEF x ^= y ; x == 1
اعلان یک
متغیر با
EXPORT DEF یا EXPORT
REDEF آن را
همزمان
تعریف و
صادر (EXPORT)
میکند. (به
صادر
و وارد کردن
نمادها (Exporting and importing
symbols) در زیر
مراجعه
کنید).
ثابتهای عددی (Numeric constants)
دستور
EQU برای
تعریف
نمادهای
ثابت عددی
استفاده
میشود.
برخلاف ‘=’
در بالا،
ثابتهایی
که به این
روش تعریف
میشوند را
نمیتوان
دوباره
تعریف کرد.
این
ثابتها
میتوانند
برای
مقادیر
بدون تغییر
مانند
ویژگیهای
سختافزار
استفاده
شوند.
def SCREEN_WIDTH equ 160 ; In pixels def SCREEN_HEIGHT equ 144
توجه
داشته
باشید که
قرار دادن
دونقطه
‘:’ پس از
نام مجاز
نیست.
اگر
واقعاً
نیاز
دارید،
کلمه کلیدی
REDEF یک
نماد ثابت
عددی را
تعریف یا
دوباره
تعریف
میکند.
(همچنین
میتوان از
آن برای
متغیرها
استفاده
کرد، اگرچه
ضروری نیست
زیرا آنها
خود
تغییرپذیر
هستند.) این
میتواند
به عنوان
مثال برای
بهروزرسانی
یک ثابت با
استفاده از
یک ماکرو،
بدون
تغییرپذیر
کردن آن در
حالت کلی،
استفاده
شود.
def NUM_ITEMS equ 0
MACRO add_item
redef NUM_ITEMS equ NUM_ITEMS + 1
def ITEM_{02x:NUM_ITEMS} equ \1
ENDM
add_item 1
add_item 4
add_item 9
add_item 16
assert NUM_ITEMS == 4
assert ITEM_04 == 16
اعلان یک
ثابت عددی
با EXPORT DEF یا
EXPORT REDEF آن را
همزمان
تعریف و
صادر (EXPORT)
میکند. (به
صادر
و وارد کردن
نمادها (Exporting and importing
symbols) در زیر
مراجعه
کنید).
ثابتهای آفست (Offset constants)
گروه دستورات RS روشی مفید برای تعریف آفستهای ساختار است:
RSRESET DEF str_pStuff RW 1 DEF str_tData RB 256 DEF str_bCount RB 1 DEF str_SIZEOF RB 0
این مثال چهار ثابت را تعریف میکند گویی که به شکل زیر تعریف شده باشند:
DEF str_pStuff EQU 0 DEF str_tData EQU 2 DEF str_bCount EQU 258 DEF str_SIZEOF EQU 259
پنج دستور در گروه دستورات RS وجود دارد:
| دستور | معنی |
RSRESET |
معادل ‘RSSET
0’. |
RSSET
constexpr |
شمارنده
_RS را روی
constexpr تنظیم
میکند. |
DEF
name RB
constexpr |
مقدار name
را برابر
_RS قرار
میدهد و
سپس constexpr را
به _RS
اضافه
میکند. |
DEF
name RW
constexpr |
مقدار name
را برابر
_RS قرار
میدهد و
سپس constexpr
* 2 را به
_RS اضافه
میکند. |
DEF
name RL
constexpr |
مقدار name
را برابر
_RS قرار
میدهد و
سپس constexpr
* 4 را به
_RS اضافه
میکند. |
اگر
آرگومان
constexpr برای
RB ، RW ، یا
RL حذف
شود، مقدار
آن 1 فرض
میشود.
توجه
داشته
باشید که
قرار دادن
دونقطه
‘:’ پس از
نام مجاز
نیست.
اعلان یک
ثابت آفست
با EXPORT DEF آن
را همزمان
تعریف و
صادر (EXPORT)
میکند. (به
صادر
و وارد کردن
نمادها (Exporting and importing
symbols) در زیر
مراجعه
کنید).
ثابتهای رشتهای (String constants)
دستور
EQUS برای
تعریف
نمادهای
ثابت
رشتهای
استفاده
میشود. هر
جا که
اسمبلر یک
ثابت
رشتهای را
بخواند، آن
نماد بسط
داده
میشود:
نام نماد با
محتوای آن
جایگزین
میشود،
مشابه #define
در زبان
برنامهنویسی
C. این بسط در
چند زمینه
غیرفعال
است: ‘DEF(name)
،’ ‘DEF name EQU/=/EQUS/etc ...
،’ ‘REDEF name EQU/=/EQUS/etc
... ،’ ‘FOR name, ...
،’ ‘PURGE name
،’ و ‘MACRO
name’
ثابتهای
رشتهای را
در نامهای
خود بسط
نخواهند
داد. بسط
همچنین در
صورتی که
نام ثابت
رشتهای یک
شناسه خام
با پیشوند
هش ‘#’ باشد،
غیرفعال
است.
DEF COUNTREG EQUS "[hl+]"
ld a, COUNTREG
DEF PLAYER_NAME EQUS "\"John\""
db PLAYER_NAME
این به صورت زیر تفسیر خواهد شد:
ld a, [hl+] db "John"
ثابتهای رشتهای همچنین میتوانند برای تعریف ماکروهای کوچک تکخطی استفاده شوند:
DEF pusha EQUS "push af\npush bc\npush de\npush hl\n"
توجه
داشته
باشید که
قرار دادن
دونقطه
‘:’ پس از
نام مجاز
نیست.
ثابتهای
رشتهای،
مانند
ثابتهای
عددی،
نمیتوانند
دوباره
تعریف شوند.
با این حال،
کلمه کلیدی
REDEF یک
نماد ثابت
رشتهای را
تعریف یا
دوباره
تعریف
میکند. به
عنوان
مثال:
DEF s EQUS "Hello, "
REDEF s EQUS "{s}world!"
; prints "Hello, world!"
PRINTLN "{s}\n"
ثابتهای رشتهای را نمیتوان صادر یا وارد کرد.
نکته
مهم:
هنگامی که
یک ثابت
رشتهای
بسط داده
میشود،
بسط آن ممکن
است شامل
ثابت
رشتهای
دیگری باشد
که آن نیز
بسط داده
خواهد شد و
ممکن است
بازگشتی
باشد. اگر
این امر یک
حلقه
بینهایت
ایجاد کند،
پس از
رسیدن به یک
عمق معین با
خطا متوقف
میشود
(گزینه خط
فرمان -r
در rgbasm(1)
را ببینید).
همین مشکل
در صورتی که
بسط یک ثابت
رشتهای
ماکرویی را
فراخوانی
کند که خود
بسط
مییابد،
میتواند
رخ دهد.
ماکروها (Macros)
یکی از
بهترین
ویژگیهای
یک اسمبلر
توانایی
نوشتن
ماکروها
برای آن است.
ماکروها را
میتوان با
آرگومانها
فراخوانی
کرد و آنها
میتوانند
بسته به
ورودی با
استفاده از
ساختارهای
IF واکنش
نشان دهند.
MACRO my_macro
ld a, 80
call MyFunc
ENDM
مثال بالا
‘my_macro’ را
به عنوان یک
ماکروی
جدید تعریف
میکند.
ثابتهای
رشتهای در
نام ماکرو
بسط داده
نمیشوند.
ماکروها را نمیتوان صادر یا وارد کرد.
تودرتو کردن تعاریف ماکرو امکانپذیر نیست، بنابراین این مورد کار نخواهد کرد:
MACRO outer
MACRO inner
PRINTLN "Hello!"
ENDM ; this actually ends the 'outer' macro...
ENDM ; ...and then this is a syntax error!
اما
میتوانید
با استفاده
از EQUS این
محدودیت را
دور بزنید،
بنابراین
این مورد
کار خواهد
کرد:
MACRO outer
DEF definition EQUS "MACRO inner\nPRINTLN \"Hello!\"\nENDM"
definition
PURGE definition
ENDM
اطلاعات بیشتر درباره نحوه تعریف و فراخوانی ماکروها در زبان ماکرو (THE MACRO LANGUAGE) در زیر توضیح داده شده است.
صادر و وارد کردن نمادها (Exporting and importing symbols)
وارد و صادر کردن نمادها قابلیتی است که وقتی پروژه شما چندین فایل منبع را در بر میگیرد، بسیار مفید است؛ به عنوان مثال، زمانی که نیاز دارید به روال تعریفشده در فایل دیگری پرش کنید.
صادر کردن
نمادها
باید به
صورت دستی
انجام شود،
در حالی که
وارد کردن
در صورتی که
نمادی
را پیدا کند
که از آن
اطلاعی
ندارد به
صورت
خودکار
انجام
میشود.
دستور زیر باعث میشود symbol1 ، symbol2 و غیره در طول فرآیند پیوند برای فایلهای دیگر قابل دسترسی باشند:
EXPORT
symbol1 [, symbol2,
...]به عنوان مثال، اگر سه فایل زیر را داشته باشید:
‘a.asm’:
SECTION "a", WRAM0 LabelA:
‘b.asm’:
SECTION "b", WRAM0
ExportedLabelB1::
ExportedLabelB2:
EXPORT ExportedLabelB2
‘c.asm’:
SECTION "C", ROM0[0]
dw LabelA
dw ExportedLabelB1
dw ExportedLabelB2
آنگاه
‘c.asm’
میتواند
از ‘ExportedLabelB1’
و ‘ExportedLabelB2’
استفاده
کند، اما
نمیتواند
از ‘LabelA’
استفاده
کند،
بنابراین
پیوند دادن
آنها با
یکدیگر
شکست خواهد
خورد:
$ rgbasm -o a.o a.asm
$ rgbasm -o b.o b.asm
$ rgbasm -o c.o c.asm
$ rgblink a.o b.o c.o
error: Undefined symbol "LabelA"
at c.asm(2)
Linking failed with 1 error
پاکسازی نمادها (Purging symbols)
دستور
PURGE به شما
امکان
میدهد یک
نماد را
کاملاً از
جدول
نمادها حذف
کنید، گویی
که هرگز
تعریف نشده
است.
DEF value EQU 42 PURGE value DEF value EQUS "I'm a string now" ASSERT DEF(value) PURGE value ASSERT !DEF(value)
هنگام پاکسازی نمادهایی که در دادههای بخش ارجاع داده شدهاند، یا صادر شدهاند، بسیار مراقب باشید، زیرا اگر چیزی به نماد گمشده وابسته باشد (به عنوان مثال، عباراتی که پیونددهنده نیاز به محاسبه آنها دارد)، ممکن است منجر به خطاهای غیرقابل پیشبینی شود. پاکسازی برچسبها به هیچ وجه توصیه نمیشود.
ثابتهای رشتهای درون نامهای نماد بسط داده نمیشوند.
نمادهای از پیش تعریفشده (Predeclared symbols)
نمادهای زیر توسط اسمبلر تعریف شدهاند:
| نام | نوع | محتویات |
@ |
EQU |
مقدار PC (اساساً، آدرس حافظه فعلی) |
| . | EQUS |
حوزه برچسب سراسری فعلی |
.. |
EQUS |
حوزه برچسب محلی فعلی |
__SCOPE__ |
EQUS |
درونیترین سطح حوزه برچسب فعلی (خالی،. ، یا ..) |
_RS |
= |
شمارنده _RS |
_NARG |
EQU |
تعداد
آرگومانهای
ارسالی به
ماکرو،
بهروزرسانی
شده توسط
SHIFT |
__ISO_8601_LOCAL__ |
EQUS |
برچسب زمانی ISO 8601 (محلی) |
__ISO_8601_UTC__ |
EQUS |
برچسب زمانی ISO 8601 (UTC) |
__UTC_YEAR__ |
EQU |
سال جاری |
__UTC_MONTH__ |
EQU |
شماره ماه جاری، 1–12 |
__UTC_DAY__ |
EQU |
روز جاری از ماه، 1–31 |
__UTC_HOUR__ |
EQU |
ساعت فعلی، 0–23 |
__UTC_MINUTE__ |
EQU |
دقیقه فعلی، 0–59 |
__UTC_SECOND__ |
EQU |
ثانیه فعلی، 0–59 |
__RGBDS_MAJOR__ |
EQU |
شماره نسخه اصلی RGBDS |
__RGBDS_MINOR__ |
EQU |
شماره نسخه فرعی RGBDS |
__RGBDS_PATCH__ |
EQU |
شماره نسخه وصله RGBDS |
__RGBDS_RC__ |
EQU |
شناسه نامزد انتشار RGBDS، برای نسخههای نهایی تعریف نشده است |
__RGBDS_VERSION__ |
EQUS |
نسخه RGBDS،
همانطور که
توسط ‘rgbasm --version
چاپ
میشود’ |
مقادیر
زمان فعلی
از متغیر
محیطی
SOURCE_DATE_EPOCH در
صورتی که به
عنوان یک
برچسب
زمانی
یونیکس
تعریف شده
باشد،
گرفته
خواهد شد. به
مشخصات در
reproducible-builds.org
مراجعه
کنید.
تعریف دادهها (DEFINING DATA)
تعریف دادههای ثابت در ROM (Defining constant data in ROM)
دستور
DB فهرستی
از بایتها
را تعریف
میکند که
در تصویر
نهایی
ذخیره
خواهند شد.
ایدهآل
برای
جدولها و
متن.
DB 1,2,3,4,"This is a string"
همچنین،
میتوانید
از DW برای
ذخیره
فهرستی از
کلمات (16
بیتی) یا از
DL برای
ذخیره
فهرستی از
کلمات
مضاعف/طولانی
(32 بیتی)
استفاده
کنید. هر دوی
این
دستورات
دادههای
خود را با
ترتیب بایت
اندیان
کوچک (little-endian)
مینویسند؛
به عنوان
مثال، ‘dw
$CAFE’ معادل
‘db $FE, $CA’ است
و نه ‘db $CA,
$FE’.
رشتهها تا حدودی به طور ویژه مدیریت میشوند: آنها ابتدا تحت تبدیل نگاشت نویسه قرار میگیرند (به Character maps مراجعه کنید)، سپس هر نویسه حاصل به صورت مجزا خروجی داده میشود. به عنوان مثال، تحت نگاشت نویسه پیشفرض، دو خط زیر یکسان هستند:
DW "Hello!" DW "H", "e", "l", "l", "o", "!"
اگر این رفتار ویژه را نمیخواهید، رشته را در پرانتز قرار دهید.
دستور
DS همچنین
میتواند
برای پر
کردن
ناحیهای
از حافظه با
مقادیر
تکراری
استفاده
شود. به
عنوان
مثال:
; outputs 3 bytes: $AA, $AA, $AA DS 3, $AA ; outputs 7 bytes: $BB, $CC, $BB, $CC, $BB, $CC, $BB DS 7, $BB, $CC
همچنین
میتوانید
از DB ، DW و
DL بدون
آرگومان
استفاده
کنید. این
دقیقاً
مانند DS 1 ، DS
2 و DS 4 به
ترتیب عمل
میکند. در
نتیجه، DB
، DW و DL
بدون
آرگومان را
میتوان در
بخشهای
WRAM0 / WRAMX /
HRAM / VRAM /
SRAM
استفاده
کرد.
شامل کردن فایلهای داده دودویی (Including binary data files)
شما
احتمالاً
دادههای
گرافیکی،
مراحل بازی
و غیره
دارید که
مایلید
آنها را
شامل کنید.
از INCBIN
برای شامل
کردن یک
فایل
دودویی خام
همانطور
که هست
استفاده
کنید. اگر
فایل در
دایرکتوری
فعلی یافت
نشود،
فهرست
مسیرهای
شاملسازی
ارائهشده
به گزینه
-I دستور
rgbasm(1) در
خط فرمان
جستجو
خواهد شد.
INCBIN "titlepic.bin" INCBIN "sprites/hero.bin"
همچنین
میتوانید
تنها بخشی
از یک فایل
را با INCBIN
شامل کنید.
مثال زیر 256
بایت از data.bin را
با شروع از
بایت 78 شامل
میکند.
INCBIN "data.bin", 78, 256
آرگومان طول اختیاری است. اگر تنها موقعیت شروع مشخص شود، بایتها از موقعیت شروع تا انتهای فایل شامل خواهند شد.
تخصیص ایستا فضا در RAM (Statically allocating space in RAM)
دستور
DS تعداد
مشخصی بایت
خالی را به
صورت ایستا
تخصیص
میدهد. این
روش ترجیحی
برای تخصیص
فضا در یک
بخش RAM است.
همچنین
میتوانید
به جای آن از
DB ، DW و DL
بدون هیچ
آرگومانی
استفاده
کنید (به
تعریف
دادههای
ثابت در ROM (Defining constant
data in ROM) در زیر
مراجعه
کنید).
DS 42 ; Allocates 42 bytes
فضای خالی
در بخشهای
RAM مقداردهی
اولیه
نخواهد شد.
در بخشهای
ROM، با مقدار
ارسالشده
به گزینه خط
فرمان -p
پر خواهد
شد، به جز
هنگام
استفاده از
همپوشانیها
با -O.
به جای
تعداد دقیق
بایتها،
میتوانید
ALIGN[align,
offset] را
مشخص کنید
تا هر تعداد
بایت که
برای تراز
کردن
دادههای
بعدی لازم
است تخصیص
یابد.
بنابراین،
‘DS ALIGN[align,
offset], ...’
معادل ‘DS
n, ...’
به دنبال
‘ALIGN[align,
offset]’ است،
که در آن n
حداقل
مقدار مورد
نیاز برای
برآورده
کردن
محدودیت
ALIGN است (به
درخواست
تراز (Requesting alignment) در
زیر مراجعه
کنید). توجه
داشته
باشید که
ALIGN[align]
کوتاهشدهای
برای
ALIGN[align,
0] است.
تخصیص فضاهای همپوشا در RAM (Allocating overlapping spaces in RAM)
یونیونها به چندین تخصیص حافظه ایستا اجازه همپوشانی میدهند، مانند union در زبان C. این امر مقدار حافظه در دسترس را افزایش نمیدهد، اما اجازه میدهد از همان ناحیه حافظه برای اهداف مختلف دوباره استفاده شود.
یک یونیون
با کلمه
کلیدی UNION
شروع
میشود و با
کلمه کلیدی
متناظر
ENDU پایان
مییابد.
دستور NEXTU
هر بلوک از
تخصیصها
را جدا
میکند، و
میتوانید
هر تعداد
بار که در یک
یونیون
لازم است از
آن استفاده
کنید.
; Let's say PC == $C0DE here
UNION
; Here, PC == $C0DE
wName:: ds 10
; Now, PC == $C0E8
wNickname:: ds 10
; PC == $C0F2
NEXTU
; PC is back to $C0DE
wHealth:: dw
; PC == $C0E0
wLives:: db
; PC == $C0E1
ds 7
; PC == $C0E8
wBonus:: db
; PC == $C0E9
NEXTU
; PC is back to $C0DE again
wVideoBuffer: ds 16
; PC == $C0EE
ENDU
; Afterward, PC == $C0F2
در مثال
بالا، ‘wName ، wHealth
،’ و ‘wVideoBuffer’
همگی مقدار
یکسانی
دارند؛
همینطور
‘wNickname’ و ‘wBonus’.
بنابراین
به خاطر
داشته
باشید که
‘ld [wHealth], a’
دقیقاً به
همان دستور
‘ld [wName], a’
اسمبل
میشود.
اندازه کل این یونیون 20 بایت است، یعنی اندازه بزرگترین بلوک (اولین بلوک، شامل ‘wName’ و ‘wNickname’).
یونیونها ممکن است تودرتو باشند، به طوری که اندازه هر یونیون درونی طبق روش بالا تعیین میشود و مانند هر تخصیص دیگری روی یونیون بیرونی تأثیر میگذارد.
یونیونها
را میتوان
در هر بخشی
استفاده
کرد، اما
آنها فقط
میتوانند
شامل
دستورالعملهای
تخصیص فضا
مانند DS
باشند (به
تخصیص
ایستا فضا
در RAM (Statically allocating space in RAM)
مراجعه
کنید).
درخواست تراز (Requesting alignment)
در حالی که
دستور ALIGN
همانطور که
در SECTIONS
معرفی شد
اغلب به
همان شکل
مفید است،
گاهی اوقات
به جای آن
میخواهید
یک قطعه
داده (یا کد)
خاص در
میانه بخش
تراز شود.
این کار از
طریق
استفاده از
ALIGN align,
offset در
میانه بخش
آسانتر
شده است. این
دستور
ویژگیهای
بخش را
عطفبهماسبق
تغییر
میدهد تا
اطمینان
حاصل شود
مکانی که
دستورالعمل
ALIGN در آن
قرار دارد،
align بیت
پایینتر
آن برابر با
offset باشد.
اگر این
محدودیت
نتواند
برآورده
شود (برای
مثال به
دلیل اینکه
بخش در یک
آدرس
ناسازگار
ثابت شده
است)، خطایی
ایجاد
میشود.
توجه داشته
باشید که
ALIGN align
کوتاهشدهای
برای ALIGN
align, 0
است.
ممکن است
زمانهایی
وجود داشته
باشد که
نهتنها
بخواهید یک
محدودیت
تراز را در
مکان فعلی
مشخص کنید،
بلکه
بخواهید تا
زمانی که
محدودیت
برآورده
شود به جلو
بروید. در
این حالت،
میتوانید
از DS
ALIGN[align, offset]
برای تخصیص
هر تعداد
بایت که
برای تراز
کردن
دادههای
بعدی لازم
است
استفاده
کنید.
اگر با پرش
از هر مقدار
فضا نتوان
محدودیت را
برآورده
کرد، خطایی
ایجاد
میشود.
توجه داشته
باشید که
ALIGN[align]
کوتاهشدهای
برای
ALIGN[align,
0] است.
زبان ماکرو (THE MACRO LANGUAGE)
فراخوانی ماکروها (Invoking macros)
یک ماکرو با استفاده از نام آن در ابتدای یک خط، مانند یک دستورالعمل، به همراه هر آرگومان جدا شده با ویرگول فراخوانی میشود.
add a, b ld sp, hl my_macro ; This will be expanded sub a, 87 my_macro 42 ; So will this ret c my_macro 1, 2 ; And this
پس از
اینکه
خط
فراخوانی
ماکرو را
خواند،
بدنه ماکرو
(خطوط بین
MACRO و ENDM)
را در جای آن
بسط
میدهد.
نکته
مهم:
هنگامی که
بدنه یک
ماکرو بسط
مییابد،
بسط آن ممکن
است شامل
فراخوانی
ماکروی
دیگری باشد
که آن نیز
بسط داده
خواهد شد و
ممکن است
بازگشتی
باشد. اگر
این امر یک
حلقه
بینهایت
ایجاد کند،
پس از
رسیدن به یک
عمق معین با
خطا متوقف
میشود
(گزینه خط
فرمان -r
در rgbasm(1)
را ببینید).
همین مشکل
ممکن است در
صورتی که
بسط یک
ماکرو سپس
یک ثابت
رشتهای را
بسط دهد که
خود بسط
مییابد،
رخ دهد.
ارسال آرگومانها به ماکروها نیز امکانپذیر است!
MACRO lb
ld \1, (\2) << 8 | (\3)
ENDM
lb hl, 20, 18 ; Expands to "ld hl, ((20) << 8) | (18)"
lb de, 3 + 1, NUM**2 ; Expands to "ld de, ((3 + 1) << 8) | (NUM**2)"
شما
آرگومانها
را درون
بدنه ماکرو
با استفاده
از
دنبالههای
گریز \1 تا
\9 بسط
میدهید؛
\1 اولین
آرگومان
است، \2
دومین
آرگومان، و
به همین
ترتیب. از
آنجا که
تنها نه رقم
وجود دارد،
شما فقط
میتوانید
از نه
آرگومان
اول ماکرو
به این شکل
استفاده
کنید. برای
استفاده از
بقیه،
شماره
آرگومان را
در
قلابهای
شکسته قرار
میدهید،
مانند
\<10>.
این نحو
قلابدار
از اعداد
دهدهی و
نمادهای
عددی
پشتیبانی
میکند،
جایی که
مقادیر
منفی از
آخرین
آرگومان
شمارش
میشوند. به
عنوان
مثال،
‘\<_NARG>’ یا
‘\<-1>’
آخرین
آرگومان را
دریافت
خواهد کرد.
سایر
آرگومانهای
ماکرو و
درونیابیهای
نماد نیز
درون
قلابهای
شکسته بسط
داده
خواهند شد.
به عنوان
مثال، اگر
‘\1’
برابر با
‘13’
باشد،
آنگاه
‘\<\1>’
درون بدنه
ماکرو به
‘\<13>’ بسط
مییابد. یا
اگر ‘DEF v10 =
42’ و ‘DEF x =
10’ باشد،
آنگاه
‘\<v{d:x}>’ به
‘\<42>’ بسط
خواهد
یافت.
آرگومانهای ماکرو به صورت ثابتهای رشتهای ارسال میشوند، اگرچه نیازی به قرار دادن آنها درون علامت نقلقول نیست. بنابراین، آرگومانها به عنوان عبارت ارزیابی نمیشوند، بلکه مستقیماً درون بدنه ماکرو بسط مییابند. این بدان معنی است که آنها از تمام دنبالههای گریز رشتهها پشتیبانی میکنند (به String expressions در بالا مراجعه کنید)، و همچنین برخی از دنبالههای خاص خود را دارند:
| دنباله | معنی |
‘\,’ |
ویرگول (آرگومان را خاتمه نمیدهد) |
‘\(’ |
پرانتز باز (محتوای آرگومان را محصور نمیکند) |
‘\)’ |
پرانتز بسته (محتوای آرگومان را خاتمه نمیدهد) |
ادامههای خط طبق معمول درون ماکروها یا فهرستهای آرگومان ماکرو کار میکنند. با این حال، برخی از نویسهها باید گریز داده شوند، همانطور که در مثال زیر آمده است:
MACRO PrintMacro1
PRINTLN STRCAT(\1)
ENDM
PrintMacro1 "Hello "\, \
"world"
MACRO PrintMacro2
PRINT \1
ENDM
PrintMacro2 STRCAT("Hello ", \
"world\n")
ویرگول در
‘PrintMacro1’
باید گریز
داده شود تا
از شروع یک
آرگومان
ماکروی
دیگر
جلوگیری
کند. ویرگول
در ‘PrintMacro2’
نیازی به
گریز ندارد
زیرا داخل
پرانتز
است، مشابه
آرگومانهای
ماکرو در
زبان
برنامهنویسی
C. بکاسلش در
‘\n’ نیز
نیازی به
گریز ندارد
زیرا
لیترالهای
رشتهای
نقلقولشده
طبق معمول
درون
آرگومانهای
ماکرو کار
میکنند.
از آنجا که آرگومانهای ماکرو مستقیماً بسط مییابند، اغلب ایده خوبی است که اگر قرار است بخشی از یک عبارت عددی باشند، آنها را درون پرانتز قرار دهید. برای نمونه، موارد زیر را در نظر بگیرید:
MACRO print_double
PRINTLN \1 * 3
ENDM
print_double 1 + 2
بدنه به
‘PRINTLN 1 + 2 * 3’
بسط خواهد
یافت که
مقدار 7 را
چاپ میکند
و نه 9
همانطور که
ممکن است
انتظار
داشته
باشید.
دستورالعمل
SHIFT فقط
درون بدنه
ماکروها در
دسترس است.
این دستور
شمارههای
آرگومان را
یک واحد به
سمت چپ
منتقل
میکند،
بنابراین
آنچه \2
بود اکنون
\1 است،
آنچه \3
بود اکنون
\2 است، و
به همین
ترتیب. (آنچه
\1 بود
دیگر در
دسترس
نیست،
بنابراین
_NARG به
اندازه 1
کاهش
مییابد.)
دستور
SHIFT
همچنین
میتواند
یک پارامتر
عدد صحیح
بگیرد تا به
جای یک بار،
به آن تعداد
دفعات شیفت
را انجام
دهد. یک
پارامتر
منفی
آرگومانها
را به سمت
راست شیفت
میدهد که
میتواند
دسترسی به
موارد
شیفتدادهشده
قبلی را
دوباره به
دست آورد.
دستور
SHIFT به
ویژه در
حلقههای
REPT برای
تکرار روی
آرگومانهای
مختلف و
ارزیابی
همان بدنه
حلقه در هر
بار بسیار
مفید است.
چندین دنباله گریز وجود دارند که فقط درون بدنه یک ماکرو معتبر هستند:
| دنباله | معنی |
| ‘\1’ – ‘\9’ | آرگومانهای اول–نهم ماکرو |
‘\<...>’ |
آرگومانهای بعدی ماکرو |
‘\#’ |
همه
آرگومانهای
_NARG ماکرو،
جدا شده با
ویرگول |
‘\@’ |
پسوند نام نماد یکتا (زیر را ببینید) |
دنباله
گریز \@
اغلب در
ماکروهایی
که نمادها
را تعریف
میکنند
مفید است.
فرض کنید
ماکروی شما
به یک حلقه
از کد
اسمبلی بسط
مییابد:
MACRO loop_c_times
xor a, a
.loop
ld [hl+], a
dec c
jr nz, .loop
ENDM
اگر از این
ماکرو بیش
از یک بار در
همان حوزه
برچسب
استفاده
کنید،
‘.loop’ را
دو بار
تعریف
میکند که
یک خطا است.
برای رفع
این مشکل،
میتوانید
از \@ به
عنوان
پسوند
برچسب
استفاده
کنید:
MACRO loop_c_times_fixed
xor a, a
.loop\@
ld [hl+], a
dec c
jr nz, .loop\@
ENDM
این در هر
فراخوانی
به مقدار
متفاوتی
بسط
مییابد،
مشابه gensym
در زبان
برنامهنویسی
Lisp.
همچنین
\@ در
بلوکهای
REPT کار
میکند، و
در هر تکرار
به مقدار
متفاوتی
بسط
مییابد.
تکرار خودکار بلوکهای کد (Automatically repeating blocks of code)
فرض کنید
میخواهید
یک حلقه
وقتگیر را
بدون کپی و
چسباندن آن
باز کنید (unroll
کنید). دستور
REPT برای
این منظور
فراهم شده
است. همه چیز
بین REPT و
ENDR
متناظر، به
تعداد
مشخصی
تکرار
خواهد شد،
درست مانند
اینکه
خودتان
عملیات
کپی/چسباندن
را انجام
داده باشید.
مثال زیر
‘add a, c’ را
چهار بار
اسمبل
میکند:
REPT 4
add a, c
ENDR
همچنین
میتوانید
از REPT
برای تولید
جدولها به
صورت
بلادرنگ
استفاده
کنید:
; Generate a table of square values from 0**2 = 0 to 100**2 = 10000
DEF x = 0
REPT 101
dw x * x
DEF x += 1
ENDR
همانند
ماکروها،
میتوانید
از دنباله
گریز \@
نیز
استفاده
کنید.
بلوکهای
REPT
میتوانند
تودرتو
باشند.
یک الگوی
رایج،
تکرار یک
بلوک به
ازای هر
مقدار در یک
محدوده خاص
است. دستور
FOR برای
این هدف
سادهتر از
REPT است.
همه چیز بین
FOR و ENDR
متناظر
برای هر
مقدار از یک
نماد
دادهشده
تکرار
خواهد شد.
ثابتهای
رشتهای
درون نام
نماد بسط
داده
نمیشوند.
به عنوان
مثال، این
کد جدولی از
مقادیر
مربع از 0 تا 255
تولید
میکند:
FOR N, 256
dw N * N
ENDR
این دستور دقیقاً طوری عمل میکند که گویی چنین کدی نوشتهاید:
DEF N = 0
dw N * N
DEF N = 1
dw N * N
DEF N = 2
dw N * N
; ...
DEF N = 255
dw N * N
DEF N = 256
میتوانید
محدوده
مقادیر FOR
را سفارشی
کنید،
مشابه تابع
‘range’ در
زبان
برنامهنویسی
Python:
| کد | محدوده |
FOR
V, stop |
V از 0 تا stop افزایش مییابد |
FOR
V, start,
stop |
V از start تا stop افزایش مییابد |
FOR
V, start,
stop, step |
V از start تا stop با گام step پیش میرود |
مقدار
FOR با step
بهروزرسانی
میشود تا
زمانی که به
stop برسد یا
از آن فراتر
رود، یعنی
محدوده
نیمهباز
از start
(شامل) تا
stop
(غیرشامل) را
پوشش
میدهد.
متغیر V
در ابتدای
هر تکرار
جدید به این
مقدار
اختصاص
مییابد؛
هرگونه
تغییری که
در بدنه
حلقه FOR
در آن ایجاد
شود
بازنویسی
خواهد شد.
بنابراین
نماد V
لازم نیست
قبل از هر
تکرار حلقه
FOR از قبل
تعریف شده
باشد، اما
اگر تعریف
شده باشد
باید یک
متغیر
(متغیرها
(Variables)) باشد. به
عنوان
مثال:
FOR V, 4, 25, 5
PRINT "{d:V} "
DEF V *= 2
ENDR
PRINTLN "done {d:V}"
این قطعه کد خروجی زیر را چاپ میکند:
4 9 14 19 24 done 29
درست
مانند
بلوکهای
REPT ،
میتوانید
از دنباله
گریز \@
درون
بلوکهای
FOR
استفاده
کنید، و
آنها
میتوانند
تودرتو
باشند.
میتوانید
یک بلوک
تکرار را با
دستور BREAK
متوقف کنید.
دستور BREAK
درون یک
بلوک REPT
یا FOR
تکرار فعلی
را قطع
میکند و
دیگر تکرار
نخواهد شد.
این دستور
اجرای کد را
پس از ENDR
بلوک ادامه
خواهد داد.
به عنوان
مثال:
FOR V, 1, 100
PRINT "{d:V}"
IF V == 5
PRINT " stop! "
BREAK
ENDC
PRINT ", "
ENDR
PRINTLN "done {d:V}"
این خروجی زیر را چاپ میکند:
1, 2, 3, 4, 5 stop! done 5
اسمبل شرطی بلوکهای کد (Conditionally assembling blocks of code)
چهار
دستور IF ، ELIF
، ELSE ، و ENDC
به شما
امکان
میدهند
بسته به یک
شرط،
بخشهایی
از کد خود را
توسط
نادیده
بگیرید. این
یک قابلیت
قدرتمند
است که
معمولاً در
ماکروها
استفاده
میشود.
IF NUM < 0 PRINTLN "NUM < 0" ELIF NUM == 0 PRINTLN "NUM == 0" ELSE PRINTLN "NUM > 0" ENDC
بلوکهای
ELIF
(کوتاهشده
"else if") و ELSE
اختیاری
هستند.
بلوکهای
IF / ELIF /
ELSE / ENDC
میتوانند
تودرتو
باشند.
توجه
داشته
باشید که
اگر یک بلوک
ELSE قبل از
یک بلوک
ELIF یافت
شود، بلوک
ELIF
نادیده
گرفته
خواهد شد.
تمام
بلوکهای
ELIF باید
قبل از بلوک
ELSE قرار
گیرند.
همچنین،
اگر بیش از
یک بلوک
ELSE وجود
داشته
باشد، همه
آنها به جز
اولین مورد
نادیده
گرفته
میشوند.
شامل کردن سایر فایلهای منبع (Including other source files)
از INCLUDE
برای
پردازش یک
فایل
اسمبلی
دیگر و سپس
بازگشت به
فایل فعلی
پس از اتمام
استفاده
کنید. اگر
فایل در
دایرکتوری
فعلی یافت
نشود،
فهرست
مسیرهای
شاملسازی
ارائهشده
به گزینه
-I دستور
rgbasm(1) در
خط فرمان
جستجو
خواهد شد.
شما
میتوانید
فراخوانیهای
INCLUDE را به
صورت
نامحدود
تودرتو
کنید (یا تا
زمانی که
حافظه تمام
شود، هر
کدام که
زودتر رخ
دهد).
INCLUDE "irq.inc"
همچنین
میتوانید
قبل از فایل
منبع،
فایلی را به
طور ضمنی با
استفاده از
گزینه -P
در rgbasm(1)
شامل (INCLUDE)
کنید.
چاپ موارد هنگام اسمبل (Printing things during assembly)
دستورات
PRINT و PRINTLN
متن و
مقادیر را
در خروجی
استاندارد
چاپ
میکنند.
برای
اشکالزدایی
ماکروها یا
هر جایی که
احساس نیاز
به اعلام
اطلاعات
مهمی به خود
داشته
باشید مفید
است.
PRINT "Hello world!\n"
PRINTLN "Hello world!"
PRINT _NARG, " arguments\n"
PRINTLN "sum: ", 2+3, " product: ", 2*3
PRINTLN STRFMT("E = %f", 2.718)
سقط فرآیند اسمبل (Aborting the assembly process)
دستورات
FAIL و WARN
میتوانند
به ترتیب
برای چاپ
خطاها و
هشدارها در
طول فرآیند
اسمبل
استفاده
شوند. این به
ویژه برای
ماکروهایی
که یک
آرگومان
نامعتبر
دریافت
میکنند
مفید است.
FAIL و WARN
یک رشته را
به عنوان
تنها
آرگومان
میپذیرند
و این رشته
را به عنوان
یک خطای
معمولی
همراه با
شماره خط
چاپ
میکنند.
دستور
FAIL
بلافاصله
اسمبل کردن
را متوقف
میکند در
حالی که
WARN پیام
را نمایش
میدهد اما
پس از آن
ادامه
میدهد.
اگر نیاز
دارید
هنگام
کامپایل از
صحت
فرضیهای
اطمینان
حاصل کنید،
میتوانید
از ASSERT و
STATIC_ASSERT
استفاده
کنید.
مثالهای
نحوی در زیر
آورده شده
است:
Function:
xor a
ASSERT LOW(MyByte) == 0
ld h, HIGH(MyByte)
ld l, a
ld a, [hli]
; You can also indent this!
ASSERT BANK(OtherFunction) == BANK(Function)
call OtherFunction
; Lowercase also works
ld hl, FirstByte
ld a, [hli]
assert FirstByte + 1 == SecondByte
ld b, [hl]
ret
.end
; If you specify one, a message will be printed
STATIC_ASSERT .end - Function < 256, "Function is too large!"
اولاً،
تفاوت بین
ASSERT و
STATIC_ASSERT این
است که اولی
در صورت
امکان توسط
RGBASM ارزیابی
میشود، در
غیر این
صورت توسط
RGBLINK؛ اما دومی
فقط و فقط
توسط RGBASM
ارزیابی
میشود. اگر
RGBASM نتواند
مقدار
آرگومان را
برای STATIC_ASSERT
محاسبه
کند، خطایی
ایجاد
میکند.
ثانیاً، همانطور که در بالا نشان داده شده است، یک رشته میتواند به صورت اختیاری در انتها اضافه شود تا بینشی درباره آنچه که ادعا (assertion) در حال بررسی آن است ارائه دهد.
در نهایت،
میتوانید
یکی از
گزینههای
WARN ، FAIL یا
FATAL را به
عنوان
اولین
آرگومان
اختیاری به
ASSERT یا
STATIC_ASSERT
اضافه کنید.
اگر ادعا
شکست
بخورد،
WARN باعث
صدور یک
هشدار ساده
(کنترل شده
توسط پرچم
-Wassert در
rgbasm(1))
میشود؛
FAIL (حالت
پیشفرض)
باعث یک
خطای
غیرکشنده
میشود؛ و
FATAL
بلافاصله
فرآیند را
سقط
میکند.
متفرقه (MISCELLANEOUS)
تغییر گزینهها هنگام اسمبل (Changing options while assembling)
دستور
OPT
میتواند
برای تغییر
برخی از
گزینهها
در طول
اسمبل از
درون منبع،
به جای
تعریف
آنها در خط
فرمان،
استفاده
شود.
(rgbasm(1) را
ببینید).
دستور
OPT فهرستی
از
گزینهها
را که با
ویرگول از
هم جدا
شدهاند به
عنوان
آرگومان
خود
میپذیرد:
PUSHO
OPT g.oOX, Wdiv ; acts like command-line `-g.oOX -Wdiv`
OPT -Wdiv ; dashes before the options are optional
DW `..ooOOXX ; uses the graphics constant characters from OPT g
PRINTLN $80000000/-1 ; prints a warning about division
POPO
DW `00112233 ; uses the default graphics constant characters
PRINTLN $80000000/-1 ; no warning by default
دستور
OPT
میتواند
گزینههای
b ، g ، p ، Q ، r
، و W را
تغییر دهد.
دستورات
POPO و PUSHO
رابط
کاربری
پشته
گزینهها
را فراهم
میکنند.
PUSHO
مجموعه
فعلی
گزینهها
را در پشته
گزینهها
قرار
میدهد (push
میکند). سپس
بعداً
میتوان از
POPO برای
بازیابی
آنها
استفاده
کرد. این
قابلیت
زمانی مفید
است که
بخواهید
برخی
گزینهها
را در یک
فایل
شاملشده
تغییر دهید
و نخواهید
گزینههای
تنظیمشده
توسط
برنامهای
که فایل شما
را شامل
کرده است از
بین ببرید.
تعداد
ورودیهای
پشته تنها
با میزان
حافظه
سیستم شما
محدود
میشود.
دستور
PUSHO
همچنین
میتواند
فهرستی از
گزینهها
را که با
ویرگول از
هم جدا
شدهاند
دریافت کند
تا مجموعه
فعلی را
ذخیره کرده
و همزمان
مجموعه
آرگومانها
را اعمال
کند:
PUSHO b.#, g.oO#
DB %..####..
DW `..ooOO##
POPO
مستثنی کردن مکانها از ردیابی پشته خطا (Excluding locations from backtraces)
خطاها و
هشدارها
ردیابی
پشته (backtraces) را
چاپ
میکنند که
مکان رخ
دادن مشکل
در فایل
منبع را
نشان
میدهد، و
منشأ مشکل
را حتی از
طریق
زنجیرهای
از
مکانهای
REPT ، FOR ،
MACRO ، و
INCLUDE
ردیابی
میکند.
گاهی اوقات
مکانهایی
وجود دارند
که مایلید
نادیده
بگیرید؛ به
عنوان
مثال، یک
ماکروی
کاربردی
رایج
هنگامی که
فقط به خطی
که ماکرو در
آن استفاده
شده اهمیت
میدهید،
یا یک فایل
INCLUDE که فقط
برای شامل
کردن سایر
فایلها به
کار میرود
و صرفاً
پرکننده در
ردیابی
پشته است.
در این
موارد،
میتوانید
با یک علامت
سؤال ‘?’ بعد
از نشانه (token)،
یک مکان را
ساکت
کنید: تمام
مکانهای
ایجاد شده
توسط ‘REPT? ،’ ‘FOR?
،’ یا ‘MACRO?’
چاپ
نخواهند
شد، و هر
مکانی که
توسط یک ‘INCLUDE?
،’ یا
فراخوانی
ماکرویی که
نام آن
بلافاصله
با یک ‘?’
دنبال شود،
چاپ نخواهد
شد. به عنوان
مثال، اگر
این قطعه کد
به صورت
‘example.asm’
اسمبل شود:
MACRO lb
assert -128 <= (\2) && (\2) < 256, "\2 is not a byte"
assert -128 <= (\3) && (\3) < 256, "\3 is not a byte"
ld \1, (LOW(\2) << 8) | LOW(\3)
ENDM
SECTION "Code", ROM0
lb hl, $123, $45
این یک ردیابی پشته خطا چاپ میکند:
error: Assertion failed: $123 is not a byte
at example.asm::lb(2)
<- example.asm(7)
اما اگر
‘MACRO’ به
‘MACRO?’
تغییر
یابد، یا
‘lb hl’ به
‘lb? hl’
تغییر
یابد،
آنگاه
ردیابی
پشته خطا
مکانی درون
ماکروی
‘lb’ را
ذکر نخواهد
کرد:
error: Assertion failed: $123 is not a byte
at example.asm(7)
همچنین ببینید (SEE ALSO)
rgbasm(1), rgblink(1), rgblink(5), rgbfix(1), rgbgfx(1), gbz80(7), rgbasm-old(5), rgbds(5), rgbds(7)
تاریخچه (HISTORY)
دستور rgbasm(1) در ابتدا توسط Carsten Sørensen به عنوان بخشی از بسته ASMotor نوشته شد و بعداً در RGBDS توسط Justin Lloyd دوباره بستهبندی شد. اکنون توسط تعدادی از مشارکتکنندگان در https://github.com/gbdev/rgbds نگهداری میشود.
| August 1, 2026 | Linux 6.12.107+deb13-amd64 |