mirror of
https://github.com/cc65/cc65.git
synced 2024-06-10 13:29:50 +00:00
Compare commits
10 Commits
2d75192589
...
32e48f0d1d
Author | SHA1 | Date | |
---|---|---|---|
|
32e48f0d1d | ||
|
2c4d4d3314 | ||
|
86e5acd679 | ||
|
84153e809e | ||
|
90723d7dd1 | ||
|
f789316f86 | ||
|
270f3544b5 | ||
|
c500cb9086 | ||
|
074ec82126 | ||
|
6bcce49d5d |
4
.github/workflows/build-on-pull-request.yml
vendored
4
.github/workflows/build-on-pull-request.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
|||
run: make -j2 lib QUIET=1
|
||||
- name: Run the regression tests.
|
||||
shell: bash
|
||||
run: make test QUIET=1
|
||||
run: make -j2 test QUIET=1
|
||||
- name: Test that the samples can be built.
|
||||
run: make -C samples platforms
|
||||
- name: Test that the targettest programs can be built.
|
||||
|
@ -89,4 +89,4 @@ jobs:
|
|||
|
||||
- name: Run the regression tests (make test)
|
||||
shell: cmd
|
||||
run: make test QUIET=1 SHELL=cmd
|
||||
run: make -j2 test QUIET=1 SHELL=cmd
|
||||
|
|
|
@ -59,7 +59,7 @@ jobs:
|
|||
run: make -j2 lib QUIET=1
|
||||
- name: Run the regression tests.
|
||||
shell: bash
|
||||
run: make test QUIET=1
|
||||
run: make -j2 test QUIET=1
|
||||
- name: Test that the samples can be built.
|
||||
shell: bash
|
||||
run: make -j2 samples
|
||||
|
|
2
.github/workflows/windows-test-scheduled.yml
vendored
2
.github/workflows/windows-test-scheduled.yml
vendored
|
@ -70,7 +70,7 @@ jobs:
|
|||
- name: Run the regression tests (make test)
|
||||
if: steps.check-sha.outputs.cache-hit != 'true'
|
||||
shell: cmd
|
||||
run: make test QUIET=1 SHELL=cmd
|
||||
run: make -j2 test QUIET=1 SHELL=cmd
|
||||
|
||||
- name: Test that the samples can be built (make samples)
|
||||
if: steps.check-sha.outputs.cache-hit != 'true'
|
||||
|
|
|
@ -14,6 +14,7 @@ MEMORY {
|
|||
SEGMENTS {
|
||||
ZEROPAGE: load = ZP, type = zp, optional = yes;
|
||||
LOADADDR: load = LOADADDR, type = ro;
|
||||
EXEHDR: load = MAIN, type = ro, optional = yes;
|
||||
CODE: load = MAIN, type = ro;
|
||||
RODATA: load = MAIN, type = ro;
|
||||
DATA: load = MAIN, type = rw;
|
||||
|
|
|
@ -14,6 +14,7 @@ MEMORY {
|
|||
SEGMENTS {
|
||||
ZEROPAGE: load = ZP, type = zp, optional = yes;
|
||||
LOADADDR: load = LOADADDR, type = ro;
|
||||
EXEHDR: load = MAIN, type = ro, optional = yes;
|
||||
CODE: load = MAIN, type = ro;
|
||||
RODATA: load = MAIN, type = ro;
|
||||
DATA: load = MAIN, type = rw;
|
||||
|
|
|
@ -14,6 +14,7 @@ MEMORY {
|
|||
SEGMENTS {
|
||||
ZEROPAGE: load = ZP, type = zp, optional = yes;
|
||||
LOADADDR: load = LOADADDR, type = ro;
|
||||
EXEHDR: load = MAIN, type = ro, optional = yes;
|
||||
CODE: load = MAIN, type = ro;
|
||||
RODATA: load = MAIN, type = ro;
|
||||
DATA: load = MAIN, type = rw;
|
||||
|
|
|
@ -829,49 +829,42 @@ names like "Loop". Here is an example:
|
|||
bne @Loop ; ERROR: Unknown identifier!
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1>Unnamed labels<p>
|
||||
|
||||
If you really want to write messy code, there are also unnamed labels. These
|
||||
labels do not have a name (you guessed that already, didn't you?). A colon is
|
||||
used to mark the absence of the name.
|
||||
If you really want to write messy code, there are also unnamed labels. To define
|
||||
an unnamed label, use either <tt>@:</tt> (<tt>.LOCALCHAR</tt> is respected if it
|
||||
is set) or sole <tt>:</tt>.
|
||||
|
||||
Unnamed labels may be accessed by using the colon plus several minus or plus
|
||||
characters as a label designator. Using the '-' characters will create a back
|
||||
reference (use the n'th label backwards), using '+' will create a forward
|
||||
reference (use the n'th label in forward direction). An example will help to
|
||||
understand this:
|
||||
To reference an unnamed label, use <tt>@</tt> (<tt>.LOCALCHAR</tt> is respected
|
||||
if it is set) or <tt>:</tt> with several <tt>-</tt> or <tt>+</tt> characters.
|
||||
The <tt>-</tt> characters will create a back reference (n'th label backwards),
|
||||
the <tt>+</tt> will create a forward reference (n'th label in forward direction).
|
||||
As an alternative, angle brackets <tt><</tt> and <tt>></tt> may be used
|
||||
instead of <tt>-</tt> and <tt>+</tt> with the same meaning.
|
||||
|
||||
Example:
|
||||
|
||||
<tscreen><verb>
|
||||
: lda (ptr1),y ; #1
|
||||
cmp (ptr2),y
|
||||
bne :+ ; -> #2
|
||||
tax
|
||||
beq :+++ ; -> #4
|
||||
iny
|
||||
bne :- ; -> #1
|
||||
inc ptr1+1
|
||||
inc ptr2+1
|
||||
bne :- ; -> #1
|
||||
|
||||
: bcs :+ ; #2 -> #3
|
||||
ldx #$FF
|
||||
rts
|
||||
|
||||
: ldx #$01 ; #3
|
||||
: rts ; #4
|
||||
cpy #0
|
||||
beq @++
|
||||
@:
|
||||
sta $2007
|
||||
dey
|
||||
bne @-
|
||||
@:
|
||||
rts
|
||||
</verb></tscreen>
|
||||
|
||||
As you can see from the example, unnamed labels will make even short
|
||||
sections of code hard to understand, because you have to count labels
|
||||
to find branch targets (this is the reason why I for my part do
|
||||
prefer the "cheap" local labels). Nevertheless, unnamed labels are
|
||||
convenient in some situations, so it's your decision.
|
||||
Unnamed labels may make even short sections of code hard to understand, because
|
||||
you have to count labels to find branch targets. It's better to prefer the
|
||||
"cheap" local labels. Nevertheless, unnamed labels are convenient in some
|
||||
situations, so it's up to your discretion.
|
||||
|
||||
<em/Note:/ <ref id="scopes" name="Scopes"> organize named symbols, not
|
||||
unnamed ones, so scopes don't have an effect on unnamed labels.
|
||||
|
||||
|
||||
|
||||
<sect1>Using macros to define labels and constants<p>
|
||||
|
||||
While there are drawbacks with this approach, it may be handy in a few rare
|
||||
|
|
|
@ -364,27 +364,6 @@ static void FreeMacExp (MacExp* E)
|
|||
|
||||
|
||||
|
||||
static void MacSkipDef (unsigned Style)
|
||||
/* Skip a macro definition */
|
||||
{
|
||||
if (Style == MAC_STYLE_CLASSIC) {
|
||||
/* Skip tokens until we reach the final .endmacro */
|
||||
while (CurTok.Tok != TOK_ENDMACRO && CurTok.Tok != TOK_EOF) {
|
||||
NextTok ();
|
||||
}
|
||||
if (CurTok.Tok != TOK_EOF) {
|
||||
SkipUntilSep ();
|
||||
} else {
|
||||
Error ("'.ENDMACRO' expected");
|
||||
}
|
||||
} else {
|
||||
/* Skip until end of line */
|
||||
SkipUntilSep ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MacDef (unsigned Style)
|
||||
/* Parse a macro definition */
|
||||
{
|
||||
|
@ -393,6 +372,7 @@ void MacDef (unsigned Style)
|
|||
FilePos Pos;
|
||||
int HaveParams;
|
||||
int LastTokWasSep;
|
||||
unsigned MacroErrorCount;
|
||||
|
||||
/* For classic macros, remember if we are at the beginning of the line.
|
||||
** If the macro name and parameters pass our checks then we will be on a
|
||||
|
@ -405,27 +385,23 @@ void MacDef (unsigned Style)
|
|||
*/
|
||||
Pos = CurTok.Pos;
|
||||
|
||||
/* Remember current error count */
|
||||
MacroErrorCount = ErrorCount;
|
||||
|
||||
/* We expect a macro name here */
|
||||
if (CurTok.Tok != TOK_IDENT) {
|
||||
Error ("Identifier expected");
|
||||
MacSkipDef (Style);
|
||||
return;
|
||||
} else if (!UbiquitousIdents && FindInstruction (&CurTok.SVal) >= 0) {
|
||||
/* The identifier is a name of a 6502 instruction, which is not
|
||||
** allowed if not explicitly enabled.
|
||||
*/
|
||||
Error ("Cannot use an instruction as macro name");
|
||||
MacSkipDef (Style);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Did we already define that macro? */
|
||||
if (HT_Find (&MacroTab, &CurTok.SVal) != 0) {
|
||||
/* Macro is already defined */
|
||||
Error ("A macro named '%m%p' is already defined", &CurTok.SVal);
|
||||
/* Skip tokens until we reach the final .endmacro */
|
||||
MacSkipDef (Style);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Define the macro */
|
||||
|
@ -508,13 +484,14 @@ void MacDef (unsigned Style)
|
|||
** it will be added to the macro definition instead of closing it.
|
||||
*/
|
||||
if (CurTok.Tok == TOK_ENDMACRO && LastTokWasSep) {
|
||||
/* Done */
|
||||
/* Done: Skip the .endmacro token */
|
||||
NextTok ();
|
||||
break;
|
||||
}
|
||||
/* May not have end of file in a macro definition */
|
||||
if (CurTok.Tok == TOK_EOF) {
|
||||
PError (&Pos, "'.ENDMACRO' expected for macro '%m%p'", &M->Name);
|
||||
goto Done;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Accept a newline or end of file for new style macros */
|
||||
|
@ -597,15 +574,11 @@ void MacDef (unsigned Style)
|
|||
NextTok ();
|
||||
}
|
||||
|
||||
/* Skip the .endmacro for a classic macro */
|
||||
if (Style == MAC_STYLE_CLASSIC) {
|
||||
NextTok ();
|
||||
}
|
||||
/* Set the Incomplete flag now that parsing is done. Any errors
|
||||
** generated in this routine will result in an incomplete macro
|
||||
*/
|
||||
M->Incomplete = (MacroErrorCount != ErrorCount);
|
||||
|
||||
/* Reset the Incomplete flag now that parsing is done */
|
||||
M->Incomplete = 0;
|
||||
|
||||
Done:
|
||||
/* Switch out of raw token mode */
|
||||
LeaveRawTokenMode ();
|
||||
}
|
||||
|
|
|
@ -707,6 +707,24 @@ static void OneLine (void)
|
|||
NextTok ();
|
||||
}
|
||||
|
||||
/* Handle @-style unnamed labels */
|
||||
if (CurTok.Tok == TOK_ULABEL) {
|
||||
if (CurTok.IVal != 0) {
|
||||
Error ("Invalid unnamed label definition");
|
||||
}
|
||||
ULabDef ();
|
||||
NextTok ();
|
||||
|
||||
/* Skip the colon. If NoColonLabels is enabled, allow labels without
|
||||
** a colon if there is no whitespace before the identifier.
|
||||
*/
|
||||
if (CurTok.Tok == TOK_COLON) {
|
||||
NextTok ();
|
||||
} else if (CurTok.WS || !NoColonLabels) {
|
||||
Error ("':' expected");
|
||||
}
|
||||
}
|
||||
|
||||
/* If the first token on the line is an identifier, check for a macro or
|
||||
** an instruction.
|
||||
*/
|
||||
|
|
|
@ -1124,17 +1124,33 @@ Again:
|
|||
/* Local symbol? */
|
||||
if (C == LocalStart) {
|
||||
|
||||
/* Read the identifier. */
|
||||
ReadIdent ();
|
||||
NextChar ();
|
||||
|
||||
/* Start character alone is not enough */
|
||||
if (SB_GetLen (&CurTok.SVal) == 1) {
|
||||
Error ("Invalid cheap local symbol");
|
||||
goto Again;
|
||||
if (IsIdChar (C)) {
|
||||
/* Read a local identifier */
|
||||
CurTok.Tok = TOK_LOCAL_IDENT;
|
||||
SB_AppendChar (&CurTok.SVal, LocalStart);
|
||||
ReadIdent ();
|
||||
} else {
|
||||
/* Read an unnamed label */
|
||||
CurTok.IVal = 0;
|
||||
CurTok.Tok = TOK_ULABEL;
|
||||
|
||||
if (C == '-' || C == '<') {
|
||||
int PrevC = C;
|
||||
do {
|
||||
--CurTok.IVal;
|
||||
NextChar ();
|
||||
} while (C == PrevC);
|
||||
} else if (C == '+' || C == '>') {
|
||||
int PrevC = C;
|
||||
do {
|
||||
++CurTok.IVal;
|
||||
NextChar ();
|
||||
} while (C == PrevC);
|
||||
}
|
||||
}
|
||||
|
||||
/* A local identifier */
|
||||
CurTok.Tok = TOK_LOCAL_IDENT;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1314,22 +1330,30 @@ CharAgain:
|
|||
break;
|
||||
|
||||
case '-':
|
||||
case '<':
|
||||
{
|
||||
int PrevC = C;
|
||||
CurTok.IVal = 0;
|
||||
do {
|
||||
--CurTok.IVal;
|
||||
NextChar ();
|
||||
} while (C == '-');
|
||||
} while (C == PrevC);
|
||||
CurTok.Tok = TOK_ULABEL;
|
||||
break;
|
||||
}
|
||||
|
||||
case '+':
|
||||
case '>':
|
||||
{
|
||||
int PrevC = C;
|
||||
CurTok.IVal = 0;
|
||||
do {
|
||||
++CurTok.IVal;
|
||||
NextChar ();
|
||||
} while (C == '+');
|
||||
} while (C == PrevC);
|
||||
CurTok.Tok = TOK_ULABEL;
|
||||
break;
|
||||
}
|
||||
|
||||
case '=':
|
||||
NextChar ();
|
||||
|
|
|
@ -71,7 +71,7 @@ typedef enum token_t {
|
|||
TOK_REG, /* Sweet16 R.. register (in sweet16 mode) */
|
||||
|
||||
TOK_ASSIGN, /* := */
|
||||
TOK_ULABEL, /* :++ or :-- */
|
||||
TOK_ULABEL, /* An unnamed label */
|
||||
|
||||
TOK_EQ, /* = */
|
||||
TOK_NE, /* <> */
|
||||
|
|
|
@ -107,8 +107,12 @@ ExprNode* ULabRef (int Which)
|
|||
int Index;
|
||||
ULabel* L;
|
||||
|
||||
/* Which can never be 0 */
|
||||
PRECONDITION (Which != 0);
|
||||
/* Which should not be 0 */
|
||||
if (Which == 0) {
|
||||
Error ("Invalid unnamed label reference");
|
||||
/* We must return something valid */
|
||||
return GenCurrentPC();
|
||||
}
|
||||
|
||||
/* Get the index of the referenced label */
|
||||
if (Which > 0) {
|
||||
|
|
|
@ -14,7 +14,9 @@ WORKDIR = ../testwrk
|
|||
|
||||
.PHONY: test continue mostlyclean clean
|
||||
|
||||
test: mostlyclean continue
|
||||
test:
|
||||
@$(MAKE) mostlyclean
|
||||
@$(MAKE) continue
|
||||
|
||||
continue:
|
||||
@$(MAKE) -C asm all
|
||||
|
|
25
test/asm/listing/060-ulabel.s
Normal file
25
test/asm/listing/060-ulabel.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
; Test new-style (@:) and legacy-style (:) unnamed labels.
|
||||
; Make sure that they have identical behavior.
|
||||
|
||||
.ORG $0000
|
||||
|
||||
@: nop
|
||||
: nop
|
||||
.ASSERT @<< = $0000, error
|
||||
.ASSERT @-- = $0000, error
|
||||
.ASSERT :<< = $0000, error
|
||||
.ASSERT :-- = $0000, error
|
||||
.ASSERT @< = $0001, error
|
||||
.ASSERT @- = $0001, error
|
||||
.ASSERT :< = $0001, error
|
||||
.ASSERT :- = $0001, error
|
||||
.ASSERT @> = $0002, error
|
||||
.ASSERT @+ = $0002, error
|
||||
.ASSERT :> = $0002, error
|
||||
.ASSERT :+ = $0002, error
|
||||
.ASSERT @>> = $0003, error
|
||||
.ASSERT @++ = $0003, error
|
||||
.ASSERT :>> = $0003, error
|
||||
.ASSERT :++ = $0003, error
|
||||
@: nop
|
||||
: nop
|
Loading…
Reference in New Issue
Block a user