mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Separate multiple instructions per line with :: (#1210)
This commit is contained in:
52
man/rgbasm.5
52
man/rgbasm.5
@@ -24,35 +24,55 @@ but any program that processes RGBDS object files (described in
|
|||||||
.Xr rgbds 5 )
|
.Xr rgbds 5 )
|
||||||
can be used in its place.
|
can be used in its place.
|
||||||
.Sh SYNTAX
|
.Sh SYNTAX
|
||||||
The syntax is line-based, just as in any other assembler, meaning that you do one instruction or directive per line:
|
The syntax is line-based, just as in any other assembler.
|
||||||
|
Each line may have components in this order:
|
||||||
.Pp
|
.Pp
|
||||||
.Dl Oo Ar label Oc Oo Ar instruction Oc Oo Ar ;\ comment Oc
|
.Dl Oo Ar directive Oc Oo Ar ;\ comment Oc
|
||||||
|
.Dl Oo Ar label: Oc Oo Ar instruction Oo Ar :: instruction ... Oc Oc Oo Ar ;\ comment Oc
|
||||||
.Pp
|
.Pp
|
||||||
Example:
|
Directives are commands to the assembler itself, such as
|
||||||
.Bd -literal -offset indent
|
.Ic PRINTLN ,
|
||||||
John: ld a,87 ;Weee
|
.Ic SECTION ,
|
||||||
.Ed
|
or
|
||||||
|
.Ic OPT .
|
||||||
.Pp
|
.Pp
|
||||||
All reserved keywords (directives, mnemonics, registers, etc.) are case-insensitive;
|
Labels tie a name to a specific location within a section (see
|
||||||
all identifiers (symbol names) are case-sensitive.
|
.Sx Labels
|
||||||
|
below).
|
||||||
|
They must come first in the line.
|
||||||
|
.Pp
|
||||||
|
Instructions are assembled into Game Boy opcodes.
|
||||||
|
Multiple instructions on one line can be separated by double colons
|
||||||
|
.Ql :: .
|
||||||
|
.Pp
|
||||||
|
All reserved keywords (directives, register names, etc.) are case-insensitive;
|
||||||
|
all identifiers (labels and other symbol names) are case-sensitive.
|
||||||
.Pp
|
.Pp
|
||||||
Comments are used to give humans information about the code, such as explanations.
|
Comments are used to give humans information about the code, such as explanations.
|
||||||
The assembler
|
The assembler
|
||||||
.Em always
|
.Em always
|
||||||
ignores comments and their contents.
|
ignores comments and their contents.
|
||||||
.Pp
|
.Pp
|
||||||
There are two syntaxes for comments.
|
There are two kinds of comments, inline and block.
|
||||||
The most common is that anything that follows a semicolon
|
Inline comments are anything that follows a semicolon
|
||||||
.Ql \&;
|
.Ql \&;
|
||||||
not inside a string, is a comment until the end of the line.
|
not inside a string, until the end of the line.
|
||||||
The second is a block comment, beginning with
|
Block comments, beginning with
|
||||||
.Ql /*
|
.Ql /*
|
||||||
and ending with
|
and ending with
|
||||||
.Ql */ .
|
.Ql */ ,
|
||||||
It can be split across multiple lines, or occur in the middle of an expression:
|
can be split across multiple lines, or occur in the middle of an expression.
|
||||||
|
.Pp
|
||||||
|
An example demonstrating these syntax features:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
DEF X = /* the value of x
|
SECTION "My Code", ROM0\ \ ;\ a directive
|
||||||
should be 3 */ 3
|
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
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Sometimes lines can be too long and it may be necessary to split them.
|
Sometimes lines can be too long and it may be necessary to split them.
|
||||||
|
|||||||
@@ -1759,11 +1759,8 @@ static int yylex_SKIP_TO_ENDC(void); // forward declaration for yylex_NORMAL
|
|||||||
|
|
||||||
static int yylex_NORMAL(void)
|
static int yylex_NORMAL(void)
|
||||||
{
|
{
|
||||||
uint32_t num = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int c = nextChar();
|
int c = nextChar();
|
||||||
char secondChar;
|
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
// Ignore whitespace and comments
|
// Ignore whitespace and comments
|
||||||
@@ -1910,15 +1907,19 @@ static int yylex_NORMAL(void)
|
|||||||
return T_OP_LOGICGT;
|
return T_OP_LOGICGT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle colon, which may begin an anonymous label ref
|
case ':': // Either :, ::, or an anonymous label ref
|
||||||
|
|
||||||
case ':':
|
|
||||||
c = peek();
|
c = peek();
|
||||||
if (c != '+' && c != '-')
|
switch (c) {
|
||||||
return T_COLON;
|
case ':':
|
||||||
|
shiftChar();
|
||||||
|
return T_DOUBLE_COLON;
|
||||||
|
case '+':
|
||||||
|
case '-':
|
||||||
readAnonLabelRef(c);
|
readAnonLabelRef(c);
|
||||||
return T_ANON;
|
return T_ANON;
|
||||||
|
default:
|
||||||
|
return T_COLON;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle numbers
|
// Handle numbers
|
||||||
|
|
||||||
@@ -1931,36 +1932,37 @@ static int yylex_NORMAL(void)
|
|||||||
case '6':
|
case '6':
|
||||||
case '7':
|
case '7':
|
||||||
case '8':
|
case '8':
|
||||||
case '9':
|
case '9': {
|
||||||
num = readNumber(10, c - '0');
|
uint32_t n = readNumber(10, c - '0');
|
||||||
|
|
||||||
if (peek() == '.') {
|
if (peek() == '.') {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
yylval.constValue = readFractionalPart(num);
|
n = readFractionalPart(n);
|
||||||
} else {
|
|
||||||
yylval.constValue = num;
|
|
||||||
}
|
}
|
||||||
|
yylval.constValue = n;
|
||||||
return T_NUMBER;
|
return T_NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
case '&': // Either &=, binary AND, logical AND, or an octal constant
|
case '&': // Either &=, binary AND, logical AND, or an octal constant
|
||||||
secondChar = peek();
|
c = peek();
|
||||||
if (secondChar == '=') {
|
if (c == '=') {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_POP_ANDEQ;
|
return T_POP_ANDEQ;
|
||||||
} else if (secondChar == '&') {
|
} else if (c == '&') {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_OP_LOGICAND;
|
return T_OP_LOGICAND;
|
||||||
} else if (secondChar >= '0' && secondChar <= '7') {
|
} else if (c >= '0' && c <= '7') {
|
||||||
yylval.constValue = readNumber(8, 0);
|
yylval.constValue = readNumber(8, 0);
|
||||||
return T_NUMBER;
|
return T_NUMBER;
|
||||||
}
|
}
|
||||||
return T_OP_AND;
|
return T_OP_AND;
|
||||||
|
|
||||||
case '%': // Either %=, MOD, or a binary constant
|
case '%': // Either %=, MOD, or a binary constant
|
||||||
secondChar = peek();
|
c = peek();
|
||||||
if (secondChar == '=') {
|
if (c == '=') {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_POP_MODEQ;
|
return T_POP_MODEQ;
|
||||||
} else if (secondChar == binDigits[0] || secondChar == binDigits[1]) {
|
} else if (c == binDigits[0] || c == binDigits[1]) {
|
||||||
yylval.constValue = readBinaryNumber();
|
yylval.constValue = readBinaryNumber();
|
||||||
return T_NUMBER;
|
return T_NUMBER;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -542,7 +542,7 @@ enum {
|
|||||||
|
|
||||||
%token T_PERIOD "."
|
%token T_PERIOD "."
|
||||||
%token T_COMMA ","
|
%token T_COMMA ","
|
||||||
%token T_COLON ":"
|
%token T_COLON ":" T_DOUBLE_COLON "::"
|
||||||
%token T_LBRACK "[" T_RBRACK "]"
|
%token T_LBRACK "[" T_RBRACK "]"
|
||||||
%token T_LPAREN "(" T_RPAREN ")"
|
%token T_LPAREN "(" T_RPAREN ")"
|
||||||
%token T_NEWLINE "newline"
|
%token T_NEWLINE "newline"
|
||||||
@@ -817,7 +817,7 @@ else : T_POP_ELSE T_NEWLINE {
|
|||||||
;
|
;
|
||||||
|
|
||||||
plain_directive : label
|
plain_directive : label
|
||||||
| label cpu_command
|
| label cpu_commands
|
||||||
| label macro
|
| label macro
|
||||||
| label directive
|
| label directive
|
||||||
| assignment_directive
|
| assignment_directive
|
||||||
@@ -844,7 +844,9 @@ redef_id : T_POP_REDEF {
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
scoped_id : T_ID | T_LOCAL_ID;
|
// T_LABEL covers identifiers followed by a double colon (e.g. `call Function::ret`,
|
||||||
|
// to be read as `call Function :: ret`). This should not conflict with anything.
|
||||||
|
scoped_id : T_ID | T_LOCAL_ID | T_LABEL;
|
||||||
scoped_anon_id : scoped_id | T_ANON;
|
scoped_anon_id : scoped_id | T_ANON;
|
||||||
|
|
||||||
label : %empty
|
label : %empty
|
||||||
@@ -860,11 +862,11 @@ label : %empty
|
|||||||
| T_LABEL T_COLON {
|
| T_LABEL T_COLON {
|
||||||
sym_AddLabel($1);
|
sym_AddLabel($1);
|
||||||
}
|
}
|
||||||
| T_LOCAL_ID T_COLON T_COLON {
|
| T_LOCAL_ID T_DOUBLE_COLON {
|
||||||
sym_AddLocalLabel($1);
|
sym_AddLocalLabel($1);
|
||||||
sym_Export($1);
|
sym_Export($1);
|
||||||
}
|
}
|
||||||
| T_LABEL T_COLON T_COLON {
|
| T_LABEL T_DOUBLE_COLON {
|
||||||
sym_AddLabel($1);
|
sym_AddLabel($1);
|
||||||
sym_Export($1);
|
sym_Export($1);
|
||||||
}
|
}
|
||||||
@@ -1776,6 +1778,9 @@ sectattrs : %empty {
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
cpu_commands : cpu_command
|
||||||
|
| cpu_command T_DOUBLE_COLON cpu_commands
|
||||||
|
;
|
||||||
|
|
||||||
cpu_command : z80_adc
|
cpu_command : z80_adc
|
||||||
| z80_add
|
| z80_add
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ error: anon-label-bad.asm(2):
|
|||||||
error: anon-label-bad.asm(6):
|
error: anon-label-bad.asm(6):
|
||||||
Reference to anonymous label 2 before, when only 1 has been created so far
|
Reference to anonymous label 2 before, when only 1 has been created so far
|
||||||
error: anon-label-bad.asm(18):
|
error: anon-label-bad.asm(18):
|
||||||
syntax error, unexpected :
|
syntax error, unexpected ::
|
||||||
error: Assembly aborted (3 errors)!
|
error: Assembly aborted (3 errors)!
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
error: macro-syntax.asm(7):
|
||||||
|
Label "old" created outside of a SECTION
|
||||||
error: macro-syntax.asm(7):
|
error: macro-syntax.asm(7):
|
||||||
syntax error, unexpected MACRO
|
syntax error, unexpected MACRO
|
||||||
error: macro-syntax.asm(8):
|
error: macro-syntax.asm(8):
|
||||||
Macro argument '\1' not defined
|
Macro argument '\1' not defined
|
||||||
error: macro-syntax.asm(9):
|
error: macro-syntax.asm(9):
|
||||||
syntax error, unexpected ENDM
|
syntax error, unexpected ENDM
|
||||||
error: Assembly aborted (3 errors)!
|
error: Assembly aborted (4 errors)!
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
error: macro-syntax.asm(7):
|
||||||
|
Label "old" created outside of a SECTION
|
||||||
error: macro-syntax.asm(7):
|
error: macro-syntax.asm(7):
|
||||||
syntax error
|
syntax error
|
||||||
error: macro-syntax.asm(8):
|
error: macro-syntax.asm(8):
|
||||||
Macro argument '\1' not defined
|
Macro argument '\1' not defined
|
||||||
error: macro-syntax.asm(9):
|
error: macro-syntax.asm(9):
|
||||||
syntax error
|
syntax error
|
||||||
error: Assembly aborted (3 errors)!
|
error: Assembly aborted (4 errors)!
|
||||||
|
|||||||
11
test/asm/multiple-instructions.asm
Normal file
11
test/asm/multiple-instructions.asm
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
SECTION "test", ROM0
|
||||||
|
|
||||||
|
push hl :: pop hl :: ret
|
||||||
|
|
||||||
|
Label: nop :: call z, .local :: ld b, a
|
||||||
|
.local push bc :: jr z, Label :: pop bc
|
||||||
|
nop :: ld a, \
|
||||||
|
b :: ret
|
||||||
|
|
||||||
|
Label2::jr Label2::ret
|
||||||
|
.local2::call nz, .local2::ret
|
||||||
0
test/asm/multiple-instructions.err
Normal file
0
test/asm/multiple-instructions.err
Normal file
0
test/asm/multiple-instructions.out
Normal file
0
test/asm/multiple-instructions.out
Normal file
BIN
test/asm/multiple-instructions.out.bin
Normal file
BIN
test/asm/multiple-instructions.out.bin
Normal file
Binary file not shown.
Reference in New Issue
Block a user