Separate multiple instructions per line with :: (#1210)

This commit is contained in:
Rangi
2023-11-05 13:13:33 -05:00
committed by GitHub
parent 0afb6cd53c
commit 28358b55fe
10 changed files with 88 additions and 46 deletions

View File

@@ -24,35 +24,55 @@ but any program that processes RGBDS object files (described in
.Xr rgbds 5 )
can be used in its place.
.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
.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
Example:
.Bd -literal -offset indent
John: ld a,87 ;Weee
.Ed
Directives are commands to the assembler itself, such as
.Ic PRINTLN ,
.Ic SECTION ,
or
.Ic OPT .
.Pp
All reserved keywords (directives, mnemonics, registers, etc.) are case-insensitive;
all identifiers (symbol names) are case-sensitive.
Labels tie a name to a specific location within a section (see
.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
Comments are used to give humans information about the code, such as explanations.
The assembler
.Em always
ignores comments and their contents.
.Pp
There are two syntaxes for comments.
The most common is that anything that follows a semicolon
There are two kinds of comments, inline and block.
Inline comments are anything that follows a semicolon
.Ql \&;
not inside a string, is a comment until the end of the line.
The second is a block comment, beginning with
not inside a string, until the end of the line.
Block comments, beginning with
.Ql /*
and ending with
.Ql */ .
It can be split across multiple lines, or occur in the middle of an expression:
.Ql */ ,
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
DEF X = /* the value of x
should be 3 */ 3
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
.Ed
.Pp
Sometimes lines can be too long and it may be necessary to split them.

View File

@@ -1759,11 +1759,8 @@ static int yylex_SKIP_TO_ENDC(void); // forward declaration for yylex_NORMAL
static int yylex_NORMAL(void)
{
uint32_t num = 0;
for (;;) {
int c = nextChar();
char secondChar;
switch (c) {
// Ignore whitespace and comments
@@ -1910,15 +1907,19 @@ static int yylex_NORMAL(void)
return T_OP_LOGICGT;
}
// Handle colon, which may begin an anonymous label ref
case ':':
case ':': // Either :, ::, or an anonymous label ref
c = peek();
if (c != '+' && c != '-')
switch (c) {
case ':':
shiftChar();
return T_DOUBLE_COLON;
case '+':
case '-':
readAnonLabelRef(c);
return T_ANON;
default:
return T_COLON;
readAnonLabelRef(c);
return T_ANON;
}
// Handle numbers
@@ -1931,36 +1932,37 @@ static int yylex_NORMAL(void)
case '6':
case '7':
case '8':
case '9':
num = readNumber(10, c - '0');
case '9': {
uint32_t n = readNumber(10, c - '0');
if (peek() == '.') {
shiftChar();
yylval.constValue = readFractionalPart(num);
} else {
yylval.constValue = num;
n = readFractionalPart(n);
}
yylval.constValue = n;
return T_NUMBER;
}
case '&': // Either &=, binary AND, logical AND, or an octal constant
secondChar = peek();
if (secondChar == '=') {
c = peek();
if (c == '=') {
shiftChar();
return T_POP_ANDEQ;
} else if (secondChar == '&') {
} else if (c == '&') {
shiftChar();
return T_OP_LOGICAND;
} else if (secondChar >= '0' && secondChar <= '7') {
} else if (c >= '0' && c <= '7') {
yylval.constValue = readNumber(8, 0);
return T_NUMBER;
}
return T_OP_AND;
case '%': // Either %=, MOD, or a binary constant
secondChar = peek();
if (secondChar == '=') {
c = peek();
if (c == '=') {
shiftChar();
return T_POP_MODEQ;
} else if (secondChar == binDigits[0] || secondChar == binDigits[1]) {
} else if (c == binDigits[0] || c == binDigits[1]) {
yylval.constValue = readBinaryNumber();
return T_NUMBER;
}

View File

@@ -542,7 +542,7 @@ enum {
%token T_PERIOD "."
%token T_COMMA ","
%token T_COLON ":"
%token T_COLON ":" T_DOUBLE_COLON "::"
%token T_LBRACK "[" T_RBRACK "]"
%token T_LPAREN "(" T_RPAREN ")"
%token T_NEWLINE "newline"
@@ -817,7 +817,7 @@ else : T_POP_ELSE T_NEWLINE {
;
plain_directive : label
| label cpu_command
| label cpu_commands
| label macro
| label 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;
label : %empty
@@ -860,11 +862,11 @@ label : %empty
| T_LABEL T_COLON {
sym_AddLabel($1);
}
| T_LOCAL_ID T_COLON T_COLON {
| T_LOCAL_ID T_DOUBLE_COLON {
sym_AddLocalLabel($1);
sym_Export($1);
}
| T_LABEL T_COLON T_COLON {
| T_LABEL T_DOUBLE_COLON {
sym_AddLabel($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
| z80_add

View File

@@ -3,5 +3,5 @@ error: anon-label-bad.asm(2):
error: anon-label-bad.asm(6):
Reference to anonymous label 2 before, when only 1 has been created so far
error: anon-label-bad.asm(18):
syntax error, unexpected :
syntax error, unexpected ::
error: Assembly aborted (3 errors)!

View File

@@ -1,7 +1,9 @@
error: macro-syntax.asm(7):
Label "old" created outside of a SECTION
error: macro-syntax.asm(7):
syntax error, unexpected MACRO
error: macro-syntax.asm(8):
Macro argument '\1' not defined
error: macro-syntax.asm(9):
syntax error, unexpected ENDM
error: Assembly aborted (3 errors)!
error: Assembly aborted (4 errors)!

View File

@@ -1,7 +1,9 @@
error: macro-syntax.asm(7):
Label "old" created outside of a SECTION
error: macro-syntax.asm(7):
syntax error
error: macro-syntax.asm(8):
Macro argument '\1' not defined
error: macro-syntax.asm(9):
syntax error
error: Assembly aborted (3 errors)!
error: Assembly aborted (4 errors)!

View 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

View File

View File

Binary file not shown.