1
0
mirror of https://github.com/cc65/cc65.git synced 2024-12-23 19:29:37 +00:00

Added the optional C keyword "volatile" to the __asm__ statement grammar.

It prevents the statement's Assembly code from being optimized (e.g., moved or removed).  Optimization is disabled for that statement's entire function (other functions aren't affected).
This commit is contained in:
Greg King 2016-04-22 11:33:52 -04:00
parent 62c2177599
commit 2c7ccca210
2 changed files with 68 additions and 50 deletions

View File

@ -2,8 +2,9 @@
<article>
<title>cc65 Users Guide
<author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">
<date>2015-05-26
<author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline>
<url url="mailto:gregdk@users.sf.net" name="Greg King">
<date>2016-04-22
<abstract>
cc65 is a C compiler for 6502 targets. It supports several 6502 based home
@ -15,7 +16,6 @@ computers like the Commodore and Atari machines, but it is easily retargetable.
<!-- Begin the document -->
<sect>Overview<p>
cc65 was originally a C compiler for the Atari 8-bit machines written by
@ -564,7 +564,7 @@ and the one defined by the ISO standard:
that you must not mix pointers to those functions with pointers to
user-written, cdecl functions (the calling conventions are incompatible).
<p>
<item> The <tt/volatile/ keyword doesn't have an effect. This is not as bad
<item> The <tt/volatile/ keyword has almost no effect. That is not as bad
as it sounds, since the 6502 has so few registers that it isn't
possible to keep values in registers anyway.
<p>
@ -586,14 +586,14 @@ This cc65 version has some extensions to the ISO C standard.
file. The syntax is
<tscreen><verb>
asm (&lt;string literal&gt;[, optional parameters]) ;
asm [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
</verb></tscreen>
or
<tscreen><verb>
__asm__ (&lt;string literal&gt;[, optional parameters]) ;
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
</verb></tscreen>
The first form is in the user namespace and is disabled if the <tt/-A/
The first form is in the user namespace; and, is disabled if the <tt/-A/
switch is given.
There is a whole section covering inline assembler statements,
@ -735,6 +735,7 @@ This cc65 version has some extensions to the ISO C standard.
<p>
<sect>Predefined macros<p>
The compiler defines several macros at startup:
@ -1224,39 +1225,44 @@ The compiler allows to insert assembler statements into the output file. The
syntax is
<tscreen><verb>
asm (&lt;string literal&gt;[, optional parameters]) ;
asm [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
</verb></tscreen>
or
<tscreen><verb>
__asm__ (&lt;string literal&gt;[, optional parameters]) ;
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
</verb></tscreen>
<p>
The first form is in the user namespace and is disabled by <tt><ref
The first form is in the user namespace; and, is disabled by <tt><ref
id="option--standard" name="--standard"></tt> if the argument is not <tt/cc65/.
The asm statement may be used inside a function and on global file level. An
inline assembler statement is a primary expression, so it may also be used as
part of an expression. Please note however that the result of an expression
containing just an inline assembler statement is always of type <tt/void/.
The <tt/asm/ statement can be used only inside a function. Please note that
the result of an inline assembler expression is always of type <tt/void/.
The contents of the string literal are preparsed by the compiler and inserted
into the generated assembly output, so that the can be further processed by
the backend and especially the optimizer. For this reason, the compiler does
only allow regular 6502 opcodes to be used with the inline assembler. Pseudo
instructions (like <tt/.import/, <tt/.byte/ and so on) are <em/not/ allowed,
The contents of the string literal are preparsed by the compiler; and, inserted
into the generated assembly output, so that it can be processed further by
the backend -- and, especially the optimizer. For that reason, the compiler does
allow only regular 6502 opcodes to be used with the inline assembler. Pseudo
instructions (like <tt/.import/, <tt/.byte/, and so on) are <em/not/ allowed,
even if the ca65 assembler (which is used to translate the generated assembler
code) would accept them. The builtin inline assembler is not a replacement for
the full blown macro assembler which comes with the compiler.
code) would accept them. The built-in inline assembler is not a replacement for
the full-blown macro assembler which comes with the compiler.
Note: Inline assembler statements are subject to all optimizations done by the
compiler. There is currently no way to protect an inline assembler statement
from being moved or removed completely by the optimizer. If in doubt, check
the generated assembler output, or disable optimizations.
compiler. There currently is no way to protect an inline assembler statement
-- alone -- from being moved or removed completely by the optimizer. If in
doubt, check the generated assembler output; or, disable optimizations (for
that function).
As a shortcut, you can put the <tt/volatile/ qualifier in your <tt/asm/
statements. It will disable optimization for the functions in which those
<tt/asm volatile/ statements sit. The effect is the same as though you put
</#pragma optimize(push, off)/ above those functions, and </#pragma
optimize(pop)/ below those functions.
The string literal may contain format specifiers from the following list. For
each format specifier, an argument is expected which is inserted instead of
the format specifier before passing the assembly code line to the backend.
the format specifier, before passing the assembly code line to the backend.
<itemize>
<item><tt/%b/ - Numerical 8-bit value
@ -1269,33 +1275,33 @@ the format specifier before passing the assembly code line to the backend.
<item><tt/%%/ - The % sign itself
</itemize><p>
Using these format specifiers, you can access C <tt/#defines/, variables or
Using those format specifiers, you can access C <tt/#defines/, variables, or
similar stuff from the inline assembler. For example, to load the value of
a C <tt/#define/ into the Y register, one would use
a C <tt/#define/ into the Y index register, one would use
<tscreen><verb>
#define OFFS 23
__asm__ ("ldy #%b", OFFS);
#define OFFS 23
__asm__ ("ldy #%b", OFFS);
</verb></tscreen>
Or, to access a struct member of a static variable:
<tscreen><verb>
typedef struct {
unsigned char x;
unsigned char y;
unsigned char color;
} pixel_t;
static pixel_t pixel;
__asm__ ("ldy #%b", offsetof(pixel_t, color));
__asm__ ("lda %v,y", pixel);
typedef struct {
unsigned char x;
unsigned char y;
unsigned char color;
} pixel_t;
static pixel_t pixel;
__asm__ ("ldy #%b", offsetof(pixel_t, color));
__asm__ ("lda %v,y", pixel);
</verb></tscreen>
<p>
The next example shows how to use global variables to exchange data between C
an assembler and how to handle assembler jumps:
and assembler; and, how to handle assembler jumps:
<tscreen><verb>
unsigned char globalSubA, globalSubB, globalSubResult;
static unsigned char globalSubA, globalSubB, globalSubResult;
/* return a-b, return 255 if b>a */
unsigned char sub (unsigned char a, unsigned char b)
@ -1314,19 +1320,19 @@ an assembler and how to handle assembler jumps:
</verb></tscreen>
<p>
Arrays can also be accessed:
Arrays also can be accessed:
<tscreen><verb>
unsigned char globalSquareTable[] = {
static const unsigned char globalSquareTable[] = {
0, 1, 4, 9, 16, 25, 36, 49, 64, 81,
100, 121, 144, 169, 196, 225
};
unsigned char globalSquareA, globalSquareResult;
static unsigned char globalSquareA, globalSquareResult;
/* return a*a for a<16, else 255 */
unsigned char square (unsigned char a)
{
if (a>15){
if (a > 15) {
return 255;
}
globalSquareA = a;
@ -1339,28 +1345,30 @@ Arrays can also be accessed:
<p>
Note: Do not embed the assembler labels that are used as names of global
variables or functions into your asm statements. Code like this
variables or functions into your <tt/asm/ statements. Code such as this:
<tscreen><verb>
int foo;
int bar () { return 1; }
__asm__ ("lda _foo"); /* DON'T DO THAT! */
int bar (void) { return 1; }
...
__asm__ ("lda _foo"); /* DON'T DO THAT! */
...
__asm__ ("jsr _bar"); /* DON'T DO THAT EITHER! */
</verb></tscreen>
<p>
may stop working if the way, the compiler generates these names is changed in
a future version. Instead use the format specifiers from the table above:
might stop working if the way that the compiler generates those names is changed in
a future version. Instead, use the format specifiers from the table above:
<tscreen><verb>
__asm__ ("lda %v", foo); /* OK */
__asm__ ("lda %v", foo); /* OK */
...
__asm__ ("jsr %v", bar); /* OK */
</verb></tscreen>
<p>
<sect>Implementation-defined behavior<p>
This section describes the behavior of cc65 when the standard describes the
@ -1434,4 +1442,3 @@ freely, subject to the following restrictions:
</enum>
</article>

View File

@ -41,12 +41,14 @@
/* cc65 */
#include "asmlabel.h"
#include "codegen.h"
#include "codeseg.h"
#include "datatype.h"
#include "error.h"
#include "expr.h"
#include "function.h"
#include "litpool.h"
#include "scanner.h"
#include "segments.h"
#include "stackptr.h"
#include "symtab.h"
#include "asmstmt.h"
@ -422,6 +424,15 @@ void AsmStatement (void)
/* Skip the ASM */
NextToken ();
/* An optional volatile qualifier disables optimization for
** the entire function [same as #pragma optimize(push, off)].
*/
if (CurTok.Tok == TOK_VOLATILE) {
/* Don't optimize the Current code Segment */
CS->Code->Optimize = 0;
NextToken ();
}
/* Need left parenthesis */
if (!ConsumeLParen ()) {
return;