Fixed the ca65 Assembly-code variable ".paramcount".
Fixed how it's described in the ca65 document.
This commit is contained in:
309
doc/ca65.sgml
309
doc/ca65.sgml
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
<article>
|
<article>
|
||||||
<title>ca65 Users Guide
|
<title>ca65 Users Guide
|
||||||
<author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">
|
<author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline>
|
||||||
<date>2015-08-01
|
<url url="mailto:greg.king5@verizon.net" name="Greg King">
|
||||||
|
<date>2015-11-17
|
||||||
|
|
||||||
<abstract>
|
<abstract>
|
||||||
ca65 is a powerful macro assembler for the 6502, 65C02, and 65816 CPUs. It is
|
ca65 is a powerful macro assembler for the 6502, 65C02, and 65816 CPUs. It is
|
||||||
@@ -3935,10 +3936,10 @@ In its simplest form, a macro does not have parameters. Here's an
|
|||||||
example:
|
example:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro asr ; Arithmetic shift right
|
.macro asr ; Arithmetic shift right
|
||||||
cmp #$80 ; Put bit 7 into carry
|
cmp #$80 ; Put bit 7 into carry
|
||||||
ror ; Rotate right with carry
|
ror ; Rotate right with carry
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
The macro above consists of two real instructions, that are inserted into
|
The macro above consists of two real instructions, that are inserted into
|
||||||
@@ -3946,9 +3947,9 @@ the code, whenever the macro is expanded. Macro expansion is simply done
|
|||||||
by using the name, like this:
|
by using the name, like this:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
lda $2010
|
lda $2010
|
||||||
asr
|
asr
|
||||||
sta $2010
|
sta $2010
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
|
|
||||||
@@ -3957,15 +3958,15 @@ by using the name, like this:
|
|||||||
When using macro parameters, macros can be even more useful:
|
When using macro parameters, macros can be even more useful:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro inc16 addr
|
.macro inc16 addr
|
||||||
clc
|
clc
|
||||||
lda addr
|
lda addr
|
||||||
adc #$01
|
adc #<$0001
|
||||||
sta addr
|
sta addr
|
||||||
lda addr+1
|
lda addr+1
|
||||||
adc #$00
|
adc #>$0001
|
||||||
sta addr+1
|
sta addr+1
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
When calling the macro, you may give a parameter, and each occurrence of
|
When calling the macro, you may give a parameter, and each occurrence of
|
||||||
@@ -3973,19 +3974,19 @@ the name "addr" in the macro definition will be replaced by the given
|
|||||||
parameter. So
|
parameter. So
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
inc16 $1000
|
inc16 $1000
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
will be expanded to
|
will be expanded to
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
clc
|
clc
|
||||||
lda $1000
|
lda $1000
|
||||||
adc #$01
|
adc #<$0001
|
||||||
sta $1000
|
sta $1000
|
||||||
lda $1000+1
|
lda $1000+1
|
||||||
adc #$00
|
adc #>$0001
|
||||||
sta $1000+1
|
sta $1000+1
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
A macro may have more than one parameter, in this case, the parameters
|
A macro may have more than one parameter, in this case, the parameters
|
||||||
@@ -4006,40 +4007,40 @@ opposite.
|
|||||||
Look at this example:
|
Look at this example:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro ldaxy a, x, y
|
.macro ldaxy a, x, y
|
||||||
.ifnblank a
|
.ifnblank a
|
||||||
lda #a
|
lda #a
|
||||||
.endif
|
.endif
|
||||||
.ifnblank x
|
.ifnblank x
|
||||||
ldx #x
|
ldx #x
|
||||||
.endif
|
.endif
|
||||||
.ifnblank y
|
.ifnblank y
|
||||||
ldy #y
|
ldy #y
|
||||||
.endif
|
.endif
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
This macro may be called as follows:
|
That macro may be called as follows:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
ldaxy 1, 2, 3 ; Load all three registers
|
ldaxy 1, 2, 3 ; Load all three registers
|
||||||
|
|
||||||
ldaxy 1, , 3 ; Load only a and y
|
ldaxy 1, , 3 ; Load only a and y
|
||||||
|
|
||||||
ldaxy , , 3 ; Load y only
|
ldaxy , , 3 ; Load y only
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
There's another helper command for determining, which macro parameters are
|
There's another helper command for determining which macro parameters are
|
||||||
valid: <tt><ref id=".PARAMCOUNT" name=".PARAMCOUNT"></tt> This command is
|
valid: <tt><ref id=".PARAMCOUNT" name=".PARAMCOUNT"></tt>. That command is
|
||||||
replaced by the parameter count given, <em/including/ intermediate empty macro
|
replaced by the parameter count given, <em/including/ explicitly empty
|
||||||
parameters:
|
parameters:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
ldaxy 1 ; .PARAMCOUNT = 1
|
ldaxy 1 ; .PARAMCOUNT = 1
|
||||||
ldaxy 1,,3 ; .PARAMCOUNT = 3
|
ldaxy 1,,3 ; .PARAMCOUNT = 3
|
||||||
ldaxy 1,2 ; .PARAMCOUNT = 2
|
ldaxy 1,2 ; .PARAMCOUNT = 2
|
||||||
ldaxy 1, ; .PARAMCOUNT = 2
|
ldaxy 1, ; .PARAMCOUNT = 2
|
||||||
ldaxy 1,2,3 ; .PARAMCOUNT = 3
|
ldaxy 1,2,3 ; .PARAMCOUNT = 3
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
Macro parameters may optionally be enclosed into curly braces. This allows the
|
Macro parameters may optionally be enclosed into curly braces. This allows the
|
||||||
@@ -4047,19 +4048,19 @@ inclusion of tokens that would otherwise terminate the parameter (the comma in
|
|||||||
case of a macro parameter).
|
case of a macro parameter).
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro foo arg1, arg2
|
.macro foo arg1, arg2
|
||||||
...
|
...
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
foo ($00,x) ; Two parameters passed
|
foo ($00,x) ; Two parameters passed
|
||||||
foo {($00,x)} ; One parameter passed
|
foo {($00,x)} ; One parameter passed
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
In the first case, the macro is called with two parameters: '<tt/($00/'
|
In the first case, the macro is called with two parameters: '<tt/($00/'
|
||||||
and 'x)'. The comma is not passed to the macro, since it is part of the
|
and '<tt/x)/'. The comma is not passed to the macro, because it is part of the
|
||||||
calling sequence, not the parameters.
|
calling sequence, not the parameters.
|
||||||
|
|
||||||
In the second case, '($00,x)' is passed to the macro, this time
|
In the second case, '<tt/($00,x)/' is passed to the macro; this time,
|
||||||
including the comma.
|
including the comma.
|
||||||
|
|
||||||
|
|
||||||
@@ -4072,17 +4073,17 @@ id=".MATCH" name=".MATCH">/ and <tt/<ref id=".XMATCH" name=".XMATCH">/
|
|||||||
functions will allow you to do exactly this:
|
functions will allow you to do exactly this:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro ldax arg
|
.macro ldax arg
|
||||||
.if (.match (.left (1, {arg}), #))
|
.if (.match (.left (1, {arg}), #))
|
||||||
; immediate mode
|
; immediate mode
|
||||||
lda #<(.right (.tcount ({arg})-1, {arg}))
|
lda #<(.right (.tcount ({arg})-1, {arg}))
|
||||||
ldx #>(.right (.tcount ({arg})-1, {arg}))
|
ldx #>(.right (.tcount ({arg})-1, {arg}))
|
||||||
.else
|
.else
|
||||||
; assume absolute or zero page
|
; assume absolute or zero page
|
||||||
lda arg
|
lda arg
|
||||||
ldx 1+(arg)
|
ldx 1+(arg)
|
||||||
.endif
|
.endif
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
Using the <tt/<ref id=".MATCH" name=".MATCH">/ function, the macro is able to
|
Using the <tt/<ref id=".MATCH" name=".MATCH">/ function, the macro is able to
|
||||||
@@ -4096,11 +4097,11 @@ as end-of-list.
|
|||||||
The macro can be used as
|
The macro can be used as
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
foo: .word $5678
|
foo: .word $5678
|
||||||
...
|
...
|
||||||
ldax #$1234 ; X=$12, A=$34
|
ldax #$1234 ; X=$12, A=$34
|
||||||
...
|
...
|
||||||
ldax foo ; X=$56, A=$78
|
ldax foo ; X=$56, A=$78
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
|
|
||||||
@@ -4109,38 +4110,38 @@ The macro can be used as
|
|||||||
Macros may be used recursively:
|
Macros may be used recursively:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro push r1, r2, r3
|
.macro push r1, r2, r3
|
||||||
lda r1
|
lda r1
|
||||||
pha
|
pha
|
||||||
.if .paramcount > 1
|
.ifnblank r2
|
||||||
push r2, r3
|
push r2, r3
|
||||||
.endif
|
.endif
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
There's also a special macro to help writing recursive macros: <tt><ref
|
There's also a special macro command to help with writing recursive macros:
|
||||||
id=".EXITMACRO" name=".EXITMACRO"></tt> This command will stop macro expansion
|
<tt><ref id=".EXITMACRO" name=".EXITMACRO"></tt>. That command will stop macro
|
||||||
immediately:
|
expansion immediately:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro push r1, r2, r3, r4, r5, r6, r7
|
.macro push r1, r2, r3, r4, r5, r6, r7
|
||||||
.ifblank r1
|
.ifblank r1
|
||||||
; First parameter is empty
|
; First parameter is empty
|
||||||
.exitmacro
|
.exitmacro
|
||||||
.else
|
.else
|
||||||
lda r1
|
lda r1
|
||||||
pha
|
pha
|
||||||
.endif
|
.endif
|
||||||
push r2, r3, r4, r5, r6, r7
|
push r2, r3, r4, r5, r6, r7
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
When expanding this macro, the expansion will push all given parameters
|
When expanding that macro, the expansion will push all given parameters
|
||||||
until an empty one is encountered. The macro may be called like this:
|
until an empty one is encountered. The macro may be called like this:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
push $20, $21, $32 ; Push 3 ZP locations
|
push $20, $21, $32 ; Push 3 ZP locations
|
||||||
push $21 ; Push one ZP location
|
push $21 ; Push one ZP location
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
|
|
||||||
@@ -4151,27 +4152,27 @@ Now, with recursive macros, <tt><ref id=".IFBLANK" name=".IFBLANK"></tt> and
|
|||||||
Have a look at the inc16 macro above. Here is it again:
|
Have a look at the inc16 macro above. Here is it again:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro inc16 addr
|
.macro inc16 addr
|
||||||
clc
|
clc
|
||||||
lda addr
|
lda addr
|
||||||
adc #$01
|
adc #<$0001
|
||||||
sta addr
|
sta addr
|
||||||
lda addr+1
|
lda addr+1
|
||||||
adc #$00
|
adc #>$0001
|
||||||
sta addr+1
|
sta addr+1
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
If you have a closer look at the code, you will notice, that it could be
|
If you have a closer look at the code, you will notice, that it could be
|
||||||
written more efficiently, like this:
|
written more efficiently, like this:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro inc16 addr
|
.macro inc16 addr
|
||||||
inc addr
|
inc addr
|
||||||
bne Skip
|
bne Skip
|
||||||
inc addr+1
|
inc addr+1
|
||||||
Skip:
|
Skip:
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
But imagine what happens, if you use this macro twice? Since the label "Skip"
|
But imagine what happens, if you use this macro twice? Since the label "Skip"
|
||||||
@@ -4183,27 +4184,27 @@ local variables are replaced by a unique name in each separate macro
|
|||||||
expansion. So we can solve the problem above by using <tt/.LOCAL/:
|
expansion. So we can solve the problem above by using <tt/.LOCAL/:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro inc16 addr
|
.macro inc16 addr
|
||||||
.local Skip ; Make Skip a local symbol
|
.local Skip ; Make Skip a local symbol
|
||||||
inc addr
|
inc addr
|
||||||
bne Skip
|
bne Skip
|
||||||
inc addr+1
|
inc addr+1
|
||||||
Skip: ; Not visible outside
|
Skip: ; Not visible outside
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
Another solution is of course to start a new lexical block inside the macro
|
Another solution is of course to start a new lexical block inside the macro
|
||||||
that hides any labels:
|
that hides any labels:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro inc16 addr
|
.macro inc16 addr
|
||||||
.proc
|
.proc
|
||||||
inc addr
|
inc addr
|
||||||
bne Skip
|
bne Skip
|
||||||
inc addr+1
|
inc addr+1
|
||||||
Skip:
|
Skip:
|
||||||
.endproc
|
.endproc
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
|
|
||||||
@@ -4240,7 +4241,7 @@ different:
|
|||||||
be omitted.
|
be omitted.
|
||||||
|
|
||||||
<item> Since <tt><ref id=".DEFINE" name=".DEFINE"></tt> style macros may not
|
<item> Since <tt><ref id=".DEFINE" name=".DEFINE"></tt> style macros may not
|
||||||
contain end-of-line tokens, there are things that cannot be done. They
|
contain end-of-line tokens, there are things that cannot be done. They
|
||||||
may not contain several processor instructions for example. So, while
|
may not contain several processor instructions for example. So, while
|
||||||
some things may be done with both macro types, each type has special
|
some things may be done with both macro types, each type has special
|
||||||
usages. The types complement each other.
|
usages. The types complement each other.
|
||||||
@@ -4254,27 +4255,27 @@ To emulate assemblers that use "<tt/EQU/" instead of "<tt/=/" you may use the
|
|||||||
following <tt/.DEFINE/:
|
following <tt/.DEFINE/:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.define EQU =
|
.define EQU =
|
||||||
|
|
||||||
foo EQU $1234 ; This is accepted now
|
foo EQU $1234 ; This is accepted now
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
You may use the directive to define string constants used elsewhere:
|
You may use the directive to define string constants used elsewhere:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
; Define the version number
|
; Define the version number
|
||||||
.define VERSION "12.3a"
|
.define VERSION "12.3a"
|
||||||
|
|
||||||
; ... and use it
|
; ... and use it
|
||||||
.asciiz VERSION
|
.asciiz VERSION
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
Macros with parameters may also be useful:
|
Macros with parameters may also be useful:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.define DEBUG(message) .out message
|
.define DEBUG(message) .out message
|
||||||
|
|
||||||
DEBUG "Assembling include file #3"
|
DEBUG "Assembling include file #3"
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
Note that, while formal parameters have to be placed in braces, this is
|
Note that, while formal parameters have to be placed in braces, this is
|
||||||
@@ -4283,12 +4284,12 @@ detect the end of one parameter, only the first token is used. If you
|
|||||||
don't like that, use classic macros instead:
|
don't like that, use classic macros instead:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro DEBUG message
|
.macro DEBUG message
|
||||||
.out message
|
.out message
|
||||||
.endmacro
|
.endmacro
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
(This is an example where a problem can be solved with both macro types).
|
(That is an example where a problem can be solved with both macro types).
|
||||||
|
|
||||||
|
|
||||||
<sect1>Characters in macros<p>
|
<sect1>Characters in macros<p>
|
||||||
@@ -4308,12 +4309,12 @@ be sure to take the translation into account.
|
|||||||
<sect1>Deleting macros<p>
|
<sect1>Deleting macros<p>
|
||||||
|
|
||||||
Macros can be deleted. This will not work if the macro that should be deleted
|
Macros can be deleted. This will not work if the macro that should be deleted
|
||||||
is currently expanded as in the following non working example:
|
is currently expanded as in the following non-working example:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.macro notworking
|
.macro notworking
|
||||||
.delmacro notworking
|
.delmacro notworking
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
notworking ; Will not work
|
notworking ; Will not work
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
@@ -4324,19 +4325,19 @@ for <tt><ref id=".DEFINE" name=".DEFINE"></tt> style macros, <tt><ref
|
|||||||
id=".UNDEFINE" name=".UNDEFINE"></tt> must be used. Example:
|
id=".UNDEFINE" name=".UNDEFINE"></tt> must be used. Example:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
.define value 1
|
.define value 1
|
||||||
.macro mac
|
.macro mac
|
||||||
.byte 2
|
.byte 2
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
.byte value ; Emit one byte with value 1
|
.byte value ; Emit one byte with value 1
|
||||||
mac ; Emit another byte with value 2
|
mac ; Emit another byte with value 2
|
||||||
|
|
||||||
.undefine value
|
.undefine value
|
||||||
.delmacro mac
|
.delmacro mac
|
||||||
|
|
||||||
.byte value ; Error: Unknown identifier
|
.byte value ; Error: Unknown identifier
|
||||||
mac ; Error: Missing ":"
|
mac ; Error: Missing ":"
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
A separate command for <tt>.DEFINE</tt> style macros was necessary, because
|
A separate command for <tt>.DEFINE</tt> style macros was necessary, because
|
||||||
@@ -4348,6 +4349,7 @@ argument to <tt>.UNDEFINE</tt> is not allowed to come from another
|
|||||||
different commands increases flexibility.
|
different commands increases flexibility.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<sect>Macro packages<label id="macropackages"><p>
|
<sect>Macro packages<label id="macropackages"><p>
|
||||||
|
|
||||||
Using the <tt><ref id=".MACPACK" name=".MACPACK"></tt> directive, predefined
|
Using the <tt><ref id=".MACPACK" name=".MACPACK"></tt> directive, predefined
|
||||||
@@ -4862,6 +4864,3 @@ freely, subject to the following restrictions:
|
|||||||
|
|
||||||
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
158
src/ca65/macro.c
158
src/ca65/macro.c
@@ -147,7 +147,7 @@ static int DoMacAbort = 0;
|
|||||||
/* Counter to create local names for symbols */
|
/* Counter to create local names for symbols */
|
||||||
static unsigned LocalName = 0;
|
static unsigned LocalName = 0;
|
||||||
|
|
||||||
/* Define style macros disabled if != 0 */
|
/* Define-style macros disabled if != 0 */
|
||||||
static unsigned DisableDefines = 0;
|
static unsigned DisableDefines = 0;
|
||||||
|
|
||||||
|
|
||||||
@@ -422,8 +422,8 @@ void MacDef (unsigned Style)
|
|||||||
EnterRawTokenMode ();
|
EnterRawTokenMode ();
|
||||||
NextTok ();
|
NextTok ();
|
||||||
|
|
||||||
/* If we have a DEFINE style macro, we may have parameters in braces,
|
/* If we have a DEFINE-style macro, we may have parameters in parentheses;
|
||||||
** otherwise we may have parameters without braces.
|
** otherwise, we may have parameters without parentheses.
|
||||||
*/
|
*/
|
||||||
if (Style == MAC_STYLE_CLASSIC) {
|
if (Style == MAC_STYLE_CLASSIC) {
|
||||||
HaveParams = 1;
|
HaveParams = 1;
|
||||||
@@ -475,7 +475,7 @@ void MacDef (unsigned Style)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For class macros, we expect a separator token, for define style macros,
|
/* For classic macros, we expect a separator token, for define-style macros,
|
||||||
** we expect the closing paren.
|
** we expect the closing paren.
|
||||||
*/
|
*/
|
||||||
if (Style == MAC_STYLE_CLASSIC) {
|
if (Style == MAC_STYLE_CLASSIC) {
|
||||||
@@ -485,9 +485,9 @@ void MacDef (unsigned Style)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Preparse the macro body. We will read the tokens until we reach end of
|
/* Preparse the macro body. We will read the tokens until we reach end of
|
||||||
** file, or a .endmacro (or end of line for DEFINE style macros) and store
|
** file, or a .endmacro (or end of line for DEFINE-style macros) and store
|
||||||
** them into an token list internal to the macro. For classic macros, there
|
** them into a token list internal to the macro. For classic macros,
|
||||||
** the .LOCAL command is detected and removed at this time.
|
** the .LOCAL command is detected and removed, at this time.
|
||||||
*/
|
*/
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
@@ -752,11 +752,11 @@ ExpandParam:
|
|||||||
FreeTokNode (Mac->Final);
|
FreeTokNode (Mac->Final);
|
||||||
Mac->Final = 0;
|
Mac->Final = 0;
|
||||||
|
|
||||||
/* Problem: When a .define style macro is expanded within the call
|
/* Problem: When a .define-style macro is expanded within the call
|
||||||
** of a classic one, the latter may be terminated and removed while
|
** of a classic one, the latter may be terminated and removed while
|
||||||
** the expansion of the .define style macro is still active. Because
|
** the expansion of the .define-style macro is still active. Because
|
||||||
** line info slots are "stacked", this runs into a CHECK FAILED. For
|
** line info slots are "stacked", this runs into a CHECK FAILED. For
|
||||||
** now, we will fix that by removing the .define style macro expansion
|
** now, we will fix that by removing the .define-style macro expansion
|
||||||
** immediately, once the final token is placed. The better solution
|
** immediately, once the final token is placed. The better solution
|
||||||
** would probably be to not require AllocLineInfoSlot/FreeLineInfoSlot
|
** would probably be to not require AllocLineInfoSlot/FreeLineInfoSlot
|
||||||
** to be called in FIFO order, but this is a bigger change.
|
** to be called in FIFO order, but this is a bigger change.
|
||||||
@@ -785,72 +785,74 @@ MacEnd:
|
|||||||
static void StartExpClassic (MacExp* E)
|
static void StartExpClassic (MacExp* E)
|
||||||
/* Start expanding a classic macro */
|
/* Start expanding a classic macro */
|
||||||
{
|
{
|
||||||
token_t Term;
|
token_t Term;
|
||||||
|
|
||||||
/* Skip the macro name */
|
/* Skip the macro name */
|
||||||
NextTok ();
|
NextTok ();
|
||||||
|
|
||||||
/* Read the actual parameters */
|
/* Does this invocation have any arguments? */
|
||||||
while (!TokIsSep (CurTok.Tok)) {
|
if (!TokIsSep (CurTok.Tok)) {
|
||||||
|
|
||||||
TokNode* Last;
|
/* Read the actual parameters */
|
||||||
|
while (1) {
|
||||||
|
TokNode* Last;
|
||||||
|
|
||||||
/* Check for maximum parameter count */
|
/* Check for maximum parameter count */
|
||||||
if (E->ParamCount >= E->M->ParamCount) {
|
if (E->ParamCount >= E->M->ParamCount) {
|
||||||
ErrorSkip ("Too many macro parameters");
|
ErrorSkip ("Too many macro parameters");
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The macro may optionally be enclosed in curly braces */
|
|
||||||
Term = GetTokListTerm (TOK_COMMA);
|
|
||||||
|
|
||||||
/* Read tokens for one parameter, accept empty params */
|
|
||||||
Last = 0;
|
|
||||||
while (CurTok.Tok != Term && CurTok.Tok != TOK_SEP) {
|
|
||||||
|
|
||||||
TokNode* T;
|
|
||||||
|
|
||||||
/* Check for end of file */
|
|
||||||
if (CurTok.Tok == TOK_EOF) {
|
|
||||||
Error ("Unexpected end of file");
|
|
||||||
FreeMacExp (E);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the next token in a node */
|
|
||||||
T = NewTokNode ();
|
|
||||||
|
|
||||||
/* Insert it into the list */
|
|
||||||
if (Last == 0) {
|
|
||||||
E->Params [E->ParamCount] = T;
|
|
||||||
} else {
|
|
||||||
Last->Next = T;
|
|
||||||
}
|
|
||||||
Last = T;
|
|
||||||
|
|
||||||
/* And skip it... */
|
|
||||||
NextTok ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* One parameter more */
|
|
||||||
++E->ParamCount;
|
|
||||||
|
|
||||||
/* If the macro argument was enclosed in curly braces, end-of-line
|
|
||||||
** is an error. Skip the closing curly brace.
|
|
||||||
*/
|
|
||||||
if (Term == TOK_RCURLY) {
|
|
||||||
if (CurTok.Tok == TOK_SEP) {
|
|
||||||
Error ("End of line encountered within macro argument");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
NextTok ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for a comma */
|
/* The macro argument optionally may be enclosed in curly braces */
|
||||||
if (CurTok.Tok == TOK_COMMA) {
|
Term = GetTokListTerm (TOK_COMMA);
|
||||||
NextTok ();
|
|
||||||
} else {
|
/* Read tokens for one parameter, accept empty params */
|
||||||
break;
|
Last = 0;
|
||||||
|
while (CurTok.Tok != Term && CurTok.Tok != TOK_SEP) {
|
||||||
|
TokNode* T;
|
||||||
|
|
||||||
|
/* Check for end of file */
|
||||||
|
if (CurTok.Tok == TOK_EOF) {
|
||||||
|
Error ("Unexpected end of file");
|
||||||
|
FreeMacExp (E);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the next token in a node */
|
||||||
|
T = NewTokNode ();
|
||||||
|
|
||||||
|
/* Insert it into the list */
|
||||||
|
if (Last == 0) {
|
||||||
|
E->Params [E->ParamCount] = T;
|
||||||
|
} else {
|
||||||
|
Last->Next = T;
|
||||||
|
}
|
||||||
|
Last = T;
|
||||||
|
|
||||||
|
/* And skip it... */
|
||||||
|
NextTok ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* One parameter more */
|
||||||
|
++E->ParamCount;
|
||||||
|
|
||||||
|
/* If the macro argument was enclosed in curly braces, end-of-line
|
||||||
|
** is an error. Skip the closing curly brace.
|
||||||
|
*/
|
||||||
|
if (Term == TOK_RCURLY) {
|
||||||
|
if (CurTok.Tok == TOK_SEP) {
|
||||||
|
Error ("End of line encountered within macro argument");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
NextTok ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for a comma */
|
||||||
|
if (CurTok.Tok == TOK_COMMA) {
|
||||||
|
NextTok ();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,9 +866,9 @@ static void StartExpClassic (MacExp* E)
|
|||||||
|
|
||||||
|
|
||||||
static void StartExpDefine (MacExp* E)
|
static void StartExpDefine (MacExp* E)
|
||||||
/* Start expanding a DEFINE style macro */
|
/* Start expanding a DEFINE-style macro */
|
||||||
{
|
{
|
||||||
/* A define style macro must be called with as many actual parameters
|
/* A define-style macro must be called with as many actual parameters
|
||||||
** as there are formal ones. Get the parameter count.
|
** as there are formal ones. Get the parameter count.
|
||||||
*/
|
*/
|
||||||
unsigned Count = E->M->ParamCount;
|
unsigned Count = E->M->ParamCount;
|
||||||
@@ -876,10 +878,9 @@ static void StartExpDefine (MacExp* E)
|
|||||||
|
|
||||||
/* Read the actual parameters */
|
/* Read the actual parameters */
|
||||||
while (Count--) {
|
while (Count--) {
|
||||||
|
TokNode* Last;
|
||||||
|
|
||||||
TokNode* Last;
|
/* The macro argument optionally may be enclosed in curly braces */
|
||||||
|
|
||||||
/* The macro may optionally be enclosed in curly braces */
|
|
||||||
token_t Term = GetTokListTerm (TOK_COMMA);
|
token_t Term = GetTokListTerm (TOK_COMMA);
|
||||||
|
|
||||||
/* Check if there is really a parameter */
|
/* Check if there is really a parameter */
|
||||||
@@ -892,7 +893,6 @@ static void StartExpDefine (MacExp* E)
|
|||||||
/* Read tokens for one parameter */
|
/* Read tokens for one parameter */
|
||||||
Last = 0;
|
Last = 0;
|
||||||
do {
|
do {
|
||||||
|
|
||||||
TokNode* T;
|
TokNode* T;
|
||||||
|
|
||||||
/* Get the next token in a node */
|
/* Get the next token in a node */
|
||||||
@@ -936,7 +936,7 @@ static void StartExpDefine (MacExp* E)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Macro expansion will overwrite the current token. This is a problem
|
/* Macro expansion will overwrite the current token. This is a problem
|
||||||
** for define style macros since these are called from the scanner level.
|
** for define-style macros since these are called from the scanner level.
|
||||||
** To avoid it, remember the current token and re-insert it, once macro
|
** To avoid it, remember the current token and re-insert it, once macro
|
||||||
** expansion is done.
|
** expansion is done.
|
||||||
*/
|
*/
|
||||||
@@ -1007,8 +1007,8 @@ Macro* FindMacro (const StrBuf* Name)
|
|||||||
|
|
||||||
|
|
||||||
Macro* FindDefine (const StrBuf* Name)
|
Macro* FindDefine (const StrBuf* Name)
|
||||||
/* Try to find the define style macro with the given name and return it. If no
|
/* Try to find the define-style macro with the given name; and, return it.
|
||||||
** such macro was found, return NULL.
|
** If no such macro was found, return NULL.
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
Macro* M;
|
Macro* M;
|
||||||
@@ -1034,7 +1034,7 @@ int InMacExpansion (void)
|
|||||||
|
|
||||||
|
|
||||||
void DisableDefineStyleMacros (void)
|
void DisableDefineStyleMacros (void)
|
||||||
/* Disable define style macros until EnableDefineStyleMacros is called */
|
/* Disable define-style macros until EnableDefineStyleMacros() is called */
|
||||||
{
|
{
|
||||||
++DisableDefines;
|
++DisableDefines;
|
||||||
}
|
}
|
||||||
@@ -1042,8 +1042,8 @@ void DisableDefineStyleMacros (void)
|
|||||||
|
|
||||||
|
|
||||||
void EnableDefineStyleMacros (void)
|
void EnableDefineStyleMacros (void)
|
||||||
/* Re-enable define style macros previously disabled with
|
/* Re-enable define-style macros previously disabled with
|
||||||
** DisableDefineStyleMacros.
|
** DisableDefineStyleMacros().
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
PRECONDITION (DisableDefines > 0);
|
PRECONDITION (DisableDefines > 0);
|
||||||
|
|||||||
20
testcode/assembler/paramcount.s
Normal file
20
testcode/assembler/paramcount.s
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
; Test ca65's handling of the .paramcount read-only variable.
|
||||||
|
; .paramcount should see all given arguments, even when they are empty.
|
||||||
|
|
||||||
|
.macro push r1, r2, r3, r4, r5, r6
|
||||||
|
.out .sprintf(" .paramcount = %u", .paramcount)
|
||||||
|
.if .paramcount <> 0
|
||||||
|
.ifblank r1
|
||||||
|
.warning "r1 is blank!"
|
||||||
|
.exitmacro
|
||||||
|
.endif
|
||||||
|
lda r1
|
||||||
|
pha
|
||||||
|
|
||||||
|
push r2, r3, r4, r5, r6
|
||||||
|
.endif
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
push 1, , {}
|
||||||
|
push 1, ,
|
||||||
|
push 1
|
||||||
Reference in New Issue
Block a user