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 ) .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.

View File

@@ -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) {
case ':':
shiftChar();
return T_DOUBLE_COLON;
case '+':
case '-':
readAnonLabelRef(c);
return T_ANON;
default:
return T_COLON; return T_COLON;
}
readAnonLabelRef(c);
return T_ANON;
// 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;
} }

View File

@@ -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

View File

@@ -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)!

View File

@@ -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)!

View File

@@ -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)!

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.