adjusted docs (added !WHILE, ELSE IF, etc)

git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@251 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
marcobaye 2020-06-21 19:06:12 +00:00
parent 7f736ceccb
commit 1261960cad
9 changed files with 246 additions and 134 deletions

View File

@ -7,12 +7,12 @@
--- addressing modes --- --- addressing modes ---
If a command can be used with different addressing modes, ACME has to If an instruction can be used with different addressing modes, ACME
decide which one to use. Several commands of the 6502 CPU can be used has to decide which one to use. Several instructions of the 6502 CPU
with either "absolute" addressing or "zeropage-absolute" addressing. can be used with either "absolute" addressing or "zeropage-absolute"
The former one means there's a 16-bit argument, the latter one means addressing. The former one means there's a 16-bit argument, the latter
there's an 8-bit argument. one means there's an 8-bit argument.
And the 65816 CPU even knows some commands with 24-bit addressing... And the 65816 CPU even has some instructions with 24-bit addressing...
So how does ACME know which addressing mode to use? So how does ACME know which addressing mode to use?
The simple approach is to always use the smallest possible argument, The simple approach is to always use the smallest possible argument,
@ -29,11 +29,10 @@ The two exceptions are:
*** 1) Symbols are defined too late *** 1) Symbols are defined too late
If ACME cannot figure out the argument value in the first pass, it If ACME cannot figure out the argument value in the first pass, it
assumes that the command uses 16-bit addressing. assumes that the instruction uses 16-bit addressing.
If it later finds out that the argument only needs 8 bits, ACME gives If it later finds out that the argument only needs 8 bits, ACME gives
a warning ("using oversized addressing mode") and continues. However, a warning ("using oversized addressing mode") and continues. However,
@ -43,8 +42,7 @@ These problems can be solved by defining the symbols *before* using
them, so that the value can be figured out in the first pass. If this them, so that the value can be figured out in the first pass. If this
is not possible, you can use the postfix method, effectively exactly is not possible, you can use the postfix method, effectively exactly
defining what addressing mode to use. The postfix method is described defining what addressing mode to use. The postfix method is described
in a separate paragraph below. in a separate section below.
@ -89,8 +87,7 @@ This feature can be disabled using the "--ignore-zeroes" CLI switch.
The other possibility is to use the postfix method (described in the The other possibility is to use the postfix method (described in the
next paragraph). next section).
@ -132,7 +129,7 @@ will be assembled to
8c fd 00 ; sty $00fd 8c fd 00 ; sty $00fd
8f ff 00 00 ; sta $0000ff 8f ff 00 00 ; sta $0000ff
Postfixes given directly after the command have higher priority than Postfixes added directly to the mnemonic have higher priority than
those given to the argument. As you can see, you can add the postfix those given to the argument. As you can see, you can add the postfix
to the symbol definition as well (equivalent to leading zeros). to the symbol definition as well (equivalent to leading zeros).
@ -141,8 +138,7 @@ gives the high byte and "^" gives the bank byte of a value) to any
value will clear the argument's Force Bits 2 and 3 and set Force value will clear the argument's Force Bits 2 and 3 and set Force
Bit 1 instead. So "lda <symbol" will use 8-bit addressing, regardless Bit 1 instead. So "lda <symbol" will use 8-bit addressing, regardless
of the symbol's Force Bits. Of course, you can change this by of the symbol's Force Bits. Of course, you can change this by
postfixing the command again... :) postfixing the instruction again... :)
@ -152,7 +148,7 @@ You don't need to read this paragraph just to use ACME, I only
included it for completeness' sake. This is a description of ACME's included it for completeness' sake. This is a description of ACME's
strategy for finding the addressing mode to use: strategy for finding the addressing mode to use:
First, ACME checks whether the command has any postfix. If it has, First, ACME checks whether the instruction has any postfix. If it has,
ACME acts upon it. So postfixes have the highest priority. ACME acts upon it. So postfixes have the highest priority.
Otherwise, ACME checks whether the argument has any Force Bits set Otherwise, ACME checks whether the argument has any Force Bits set

View File

@ -29,8 +29,9 @@ Examples: !08 127, symbol, -128 ; output some values
Call: !16 EXPRESSION [, EXPRESSION]* Call: !16 EXPRESSION [, EXPRESSION]*
Purpose: Insert 16-bit values in chosen CPU's byte order. Purpose: Insert 16-bit values in chosen CPU's byte order.
Parameters: EXPRESSION: Any formula the value parser accepts. Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: "!wo", "!word" (and because all currently supported CPUs are Aliases: "!wo", "!word" (and because all currently supported
little-endian, "!le16" is in fact another alias) CPUs are little-endian, "!le16" is in fact another
alias)
Examples: !16 65535, symbol, -32768 ; output some values Examples: !16 65535, symbol, -32768 ; output some values
!wo 14, $4f35, %100101010010110, &36304, *, 'c' !wo 14, $4f35, %100101010010110, &36304, *, 'c'
!word 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2 !word 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
@ -108,12 +109,12 @@ Examples: !be32 $7fffffff, symbol, -$80000000, 14, $46a4f35
Call: !hex PAIRS_OF_HEX_DIGITS Call: !hex PAIRS_OF_HEX_DIGITS
Purpose: Insert byte values with a minimum of additional syntax. Purpose: Insert byte values with a minimum of additional
This pseudo opcode was added for easier writing of external syntax. This pseudo opcode was added for easier
source code generator tools. writing of external source code generator tools.
Parameters: PAIRS_OF_HEX_DIGITS: Just hexadecimal digits, without any Parameters: PAIRS_OF_HEX_DIGITS: Just hexadecimal digits, without
"0x" or "$" prefix. Spaces and TABs are allowed, but not any "0x" or "$" prefix. Spaces and TABs are allowed,
needed to separate the byte values. but not needed to separate the byte values.
Aliases: "!h" Aliases: "!h"
Examples: !h f0 f1 f2 f3 f4 f5 f6 f7 ; insert values 0xf0..0xf7 Examples: !h f0 f1 f2 f3 f4 f5 f6 f7 ; insert values 0xf0..0xf7
!h f0f1f2f3 f4f5f6f7 ; insert values 0xf0..0xf7 !h f0f1f2f3 f4f5f6f7 ; insert values 0xf0..0xf7
@ -136,10 +137,12 @@ Examples: !fi 256, $ff ; reserve 256 bytes
Call: !skip AMOUNT Call: !skip AMOUNT
Purpose: Advance in output buffer without starting a new segment. Purpose: Advance in output buffer without starting a new
segment.
Parameters: AMOUNT: Any formula the value parser accepts, but it Parameters: AMOUNT: Any formula the value parser accepts, but it
must be solvable even in the first pass (this limitation must be solvable even in the first pass (this
will hopefully be lifted in a future release). limitation will hopefully be lifted in a future
release).
Aliases: None Aliases: None
Examples: !skip BUFSIZE ; reserve some bytes Examples: !skip BUFSIZE ; reserve some bytes
!skip 5 ; reserve five bytes !skip 5 ; reserve five bytes
@ -155,7 +158,7 @@ Parameters: ANDVALUE: Any formula the value parser accepts, but it
it must be solvable even in the first pass. it must be solvable even in the first pass.
FILLVALUE: Any formula the value parser accepts. If it FILLVALUE: Any formula the value parser accepts. If it
is omitted, a default value is used (currently 234, is omitted, a default value is used (currently 234,
that's the 6502 CPU's NOP command). that's the opcode of the 6502 CPU's NOP instruction).
Examples: !align 255, 0 ; align to page (256 bytes) Examples: !align 255, 0 ; align to page (256 bytes)
!align 63, 0 ; align to C64 sprite block (64 bytes) !align 63, 0 ; align to C64 sprite block (64 bytes)
@ -378,22 +381,24 @@ Section: Flow control
Call: !if CONDITION { BLOCK } [ else { BLOCK } ] Call: !if CONDITION { BLOCK } [ else { BLOCK } ]
Purpose: Conditional assembly. If the given condition is true, Purpose: Conditional assembly. If the given condition is true,
the first block of statements will be parsed; the matching block of statements will be parsed;
if it isn't, the second block will be parsed instead if no condition is true, the ELSE block (if present)
(if present). will be parsed.
Parameters: CONDITION: Any formula the value parser accepts, but Parameters: CONDITION: Any formula the value parser accepts, but
it must be solvable even in the first pass. it must be solvable even in the first pass.
BLOCK: A block of assembler statements. BLOCK: A block of assembler statements.
Examples: !text "Black", 0 ; Choose wording according to Examples: ; Choose word according to "country" symbol:
!if country = uk { ; content of "country" symbol. !if country = uk {
!text "Grey" !text "Grey"
} else if country = fr {
!text "Gris"
} else if country = de {
!text "Grau"
} else { } else {
!text "Gray" !text "Gray"
} }
!byte 0
!text "White", 0
; Insert debug commands if symbol "debug" is not zero: ; Insert debug code depending on symbol "debug":
!if debug { lda #'z':jsr char_output } !if debug { lda #'z':jsr char_output }
@ -437,12 +442,26 @@ Examples: ; this was taken from <6502/std.a>:
; further instances will be skipped. ; further instances will be skipped.
} }
; include at most one driver source code:
!ifdef RAM_REU {
!src "driver_reu.a"
} else ifdef RAM_GEORAM {
!src "driver_georam.a"
} else ifdef RAM_VDCRAM {
!src "driver_vdcram.a"
} else ifdef RAM_SUPERRAM {
!src "driver_superram.a"
} else {
!src "driver_noram.a"
}
Call: !for SYMBOL, START, END { BLOCK } Call: !for SYMBOL, START, END { BLOCK }
Purpose: Looping assembly. The block of statements will be Purpose: Looping assembly. The block of statements will be
parsed a fixed number of times, as specified by the parsed a fixed number of times, as specified by the
values of START and END. For a more flexible values of START and END. For more flexible
possibility, have a look at "!do" below. possibilities, have a look at "!do" and "!while"
below.
Parameters: SYMBOL: Any valid symbol name. Parameters: SYMBOL: Any valid symbol name.
START: Any formula the value parser accepts, but it START: Any formula the value parser accepts, but it
must be solvable even in the first pass. SYMBOL will must be solvable even in the first pass. SYMBOL will
@ -492,12 +511,13 @@ Examples:
Miscellaneous: The old syntax ("!for SYMBOL, END { BLOCK }" where Miscellaneous: The old syntax ("!for SYMBOL, END { BLOCK }" where
START was always implied to be 1) is still fully START was always implied to be 1) is still fully
supported, but gives a warning to get people to change supported, but gives a warning to get people to change
to the new syntax. You can disable this warning using to the new syntax.
the "-Wno-old-for" switch, but then you will get You can disable this warning using the "--dialect" or
the "-Wno-old-for" switches, but then you will get
warnings for using the *new* syntax. warnings for using the *new* syntax.
When migrating your sources, bear in mind that it is When migrating your sources to the current syntax,
no longer possible to skip the block completely by bear in mind that it is no longer possible to skip the
specifying a loop count of zero. block completely by specifying a loop count of zero.
Also note that with the new algorithm, SYMBOL has a Also note that with the new algorithm, SYMBOL has a
different value after the block than during the last different value after the block than during the last
loop cycle, while the old algorithm kept that last loop cycle, while the old algorithm kept that last
@ -507,9 +527,9 @@ Miscellaneous: The old syntax ("!for SYMBOL, END { BLOCK }" where
Call: !set SYMBOL = VALUE Call: !set SYMBOL = VALUE
Purpose: Assign given value to symbol even if the symbol Purpose: Assign given value to symbol even if the symbol
already has a different value. Needed for loop already has a different value. Needed for loop
counters when using "!do", for example. Only use this counters when using "!do"or "!while", for example.
opcode for something else if you're sure you *really* Only use this opcode for something else if you're sure
know what you are doing... :) you *really* know what you are doing... :)
Parameters: SYMBOL: Any valid symbol name. Parameters: SYMBOL: Any valid symbol name.
VALUE: Any formula the value parser accepts. VALUE: Any formula the value parser accepts.
Example: see "!do" below Example: see "!do" below
@ -549,6 +569,34 @@ Examples: ; a loop with conditions at both start and end
!do until 3 = 4 { } while 3 < 4 !do until 3 = 4 { } while 3 < 4
Call: !while [CONDITION] { BLOCK }
Purpose: Looping assembly. The block of statements can be
parsed several times, depending on the given
condition.
The condition is parsed in every repetition before the
actual block. If it isn't met when first checked, the
block will be skipped.
Parameters: CONDITION: Any formula the value parser accepts, but
it must be solvable even in the first pass.
BLOCK: A block of assembler statements.
Examples: ; a loop with a counter
!set a = 0 ; init loop counter
!while a < 6 {
lda #a
sta label + a
!set a = a + 1
}
; a loop depending on program counter
!while * < $c000 { nop }
; a never ending loop - this will cause an error
!while 3 < 4 { nop }
; an empty loop - this will hang ACME
!while 3 != 4 { }
Call: !endoffile Call: !endoffile
Purpose: Stop processing the current source file. Using this Purpose: Stop processing the current source file. Using this
pseudo opcode you can add explanatory text inside your pseudo opcode you can add explanatory text inside your
@ -563,22 +611,24 @@ Example: rts ; some assembler mnemonic
"!eof" is reached. "!eof" is reached.
Call: !warn STRING_VALUE [, STRING_VALUE]* Call: !warn VALUE [, VALUE]*
Purpose: Show a warning during assembly. Purpose: Show a warning during assembly.
Parameters: STRING_VALUE: Can be either a string given in double Parameters: VALUE: Can be either a string given in double quotes
quotes or any formula the value parser accepts. or any formula the value parser accepts.
Numbers will be output in decimal _and_ hex format. Integer numbers will be output in both decimal _and_
hex formats.
Example: !if * > $a000 { Example: !if * > $a000 {
!warn "Program reached ROM: ", * - $a000, " bytes overlap." !warn "Program reached ROM: ", * - $a000, " bytes overlap."
} }
Call: !error STRING_VALUE [, STRING_VALUE]* Call: !error VALUE [, VALUE]*
Purpose: Generate an error during assembly (therefore, no Purpose: Generate an error during assembly (therefore, no
output file will be generated). output file will be generated).
Parameters: STRING_VALUE: Can be either a string given in double Parameters: VALUE: Can be either a string given in double quotes
quotes or any formula the value parser accepts. or any formula the value parser accepts.
Numbers will be output in decimal _and_ hex format. Integer numbers will be output in both decimal _and_
hex formats.
Example: rts ; end of some function Example: rts ; end of some function
start !source "colors.a" start !source "colors.a"
end !if end - start > 256 { end !if end - start > 256 {
@ -586,12 +636,13 @@ Example: rts ; end of some function
} }
Call: !serious STRING_VALUE [, STRING_VALUE]* Call: !serious VALUE [, VALUE]*
Purpose: Generate a serious error, immediately stopping Purpose: Generate a serious error, immediately stopping
assembly. assembly.
Parameters: STRING_VALUE: Can be either a string given in double Parameters: VALUE: Can be either a string given in double quotes
quotes or any formula the value parser accepts. or any formula the value parser accepts.
Numbers will be output in decimal _and_ hex format. Integer numbers will be output in both decimal _and_
hex formats.
Example: !source "part1.a" ; sets part1_version Example: !source "part1.a" ; sets part1_version
!source "part2.a" ; sets part2_version !source "part2.a" ; sets part2_version
!if part1_version != part2_version { !if part1_version != part2_version {
@ -611,9 +662,9 @@ Parameters: TITLE: The macro's desired name (same rules as for
could want this is beyond me). could want this is beyond me).
SYMBOL: The desired name for the parameter value at SYMBOL: The desired name for the parameter value at
call time. Normally, these parameter symbols should be call time. Normally, these parameter symbols should be
local (first character a dot), as different macro local (first character a '.' or a '@'), as different
calls will almost for sure have different parameter macro calls will almost for sure have different
values. parameter values.
If you prefix SYMBOL with a '~' character, it will be If you prefix SYMBOL with a '~' character, it will be
called by reference, not by value: Changing the value called by reference, not by value: Changing the value
inside the macro will result in the "outer" symbol to inside the macro will result in the "outer" symbol to
@ -740,8 +791,10 @@ Purpose: Set program counter to given value and start new
issued. Because some people do this overlapping issued. Because some people do this overlapping
on purpose, the warnings can be suppressed using on purpose, the warnings can be suppressed using
modifier keywords. modifier keywords.
Future versions of ACME may issue errors instead of Using the "--strict-segments" CLI switch, these
warnings. warnings can be turned onto errors. Future versions of
ACME may do that by default - so if needed, use the
modifier keywords.
Parameters: EXPRESSION: Any formula the value parser accepts, but Parameters: EXPRESSION: Any formula the value parser accepts, but
it must be solvable even in the first pass. it must be solvable even in the first pass.
MODIFIER: "overlay" or "invisible" (without quotes): MODIFIER: "overlay" or "invisible" (without quotes):
@ -847,6 +900,13 @@ Examples: ldx #.shifted_end - .shifted_start
} }
.shifted_end .shifted_end
Miscellaneous: If you need to convert a label or the program counter
from its "pseudopc" to its "real" value, you can do
that using the "&" operator. Given the example above,
the symbol ".target" will evaluate to the value $0400,
but "&.target" will evaluate to the same value as
".shifted_start" will.
---------------------------------------------------------------------- ----------------------------------------------------------------------
Section: CPU support pseudo opcodes (especially 65816 support) Section: CPU support pseudo opcodes (especially 65816 support)
@ -856,21 +916,23 @@ Call: !cpu KEYWORD [ { BLOCK } ]
Purpose: Select the processor to produce code for. If this PO Purpose: Select the processor to produce code for. If this PO
isn't used, ACME defaults to the 6502 CPU (or to the isn't used, ACME defaults to the 6502 CPU (or to the
one selected by the "--cpu" command line option). one selected by the "--cpu" command line option).
ACME will give errors if you try to assemble commands ACME will give errors if you try to assemble
the chosen CPU does not have. You can change the instructions the chosen CPU does not support. You can
chosen CPU at any time. When used with block syntax, change the chosen CPU at any time. When used with
the previously chosen CPU value is restored block syntax, the previously chosen CPU value is
afterwards. restored afterwards.
Parameters: KEYWORD: Currently valid keywords are: Parameters: KEYWORD: Currently valid keywords are:
6502 for the original MOS 6502 6502 for the original MOS 6502
6510 6502 plus undocumented opcodes nmos6502 6502 plus undocumented opcodes
65c02 6502 plus BRA,PHX/Y,PLX/Y,STZ,TRB/TSB 6510 (alias for "nmos6502")
r65c02 65c02 plus BBRx, BBSx, RMBx, SMBx 65c02 6502 plus BRA,PHX/Y,PLX/Y,STZ,TRB/TSB
w65c02 r65c02 plus STP/WAI r65c02 65c02 plus BBRx, BBSx, RMBx, SMBx
65816 65c02 plus 16/24-bit extensions w65c02 r65c02 plus STP/WAI
65ce02 r65c02 plus Z reg, long branches, ... 65816 65c02 plus 16/24-bit extensions
4502 65ce02 with MAP instead of AUG 65ce02 r65c02 plus Z reg, long branches, ...
c64dtv2 6502 plus BRA/SAC/SIR plus some of the 4502 65ce02 with MAP instead of AUG
m65 4502 plus 32-bit extensions
c64dtv2 6502 plus BRA/SAC/SIR plus some of the
undocumented opcodes undocumented opcodes
See "docs/cputypes/all.txt" for more info. See "docs/cputypes/all.txt" for more info.
BLOCK: A block of assembler statements. BLOCK: A block of assembler statements.
@ -885,7 +947,7 @@ Examples: !if cputype = $65c02 {
pla pla
} }
rts rts
!cpu 65816 ; allow 65816 commands from here on !cpu 65816 ; now allow instructions of 65816 cpu
Call: !al [ { BLOCK } ] Call: !al [ { BLOCK } ]
@ -933,6 +995,8 @@ Parameters: BLOCK: A block of assembler statements
If no block is given, only the current statement will If no block is given, only the current statement will
be affected, which should then be an explicit symbol be affected, which should then be an explicit symbol
definition. definition.
To make use of this feature, you need to use the
"-Wtype-mismatch" CLI switch.
Aliases: "!addr" Aliases: "!addr"
Examples: !addr k_chrout = $ffd2 ; this is an address Examples: !addr k_chrout = $ffd2 ; this is an address
CLEAR = 147 ; but this is not CLEAR = 147 ; but this is not
@ -958,6 +1022,10 @@ Purpose: Use PetSCII as the text conversion table. Now
superseded by the "!convtab" pseudo opcode. superseded by the "!convtab" pseudo opcode.
Old usage: !cbm ; gives "use !ct pet instead" error Old usage: !cbm ; gives "use !ct pet instead" error
Now use: !convtab pet ; does the same without error Now use: !convtab pet ; does the same without error
If you just want to assemble an old source code
without touching it, use the "--dialect" CLI switch:
Using "--dialect 0.94.6" or earlier will assemble this
pseudo opcode without throwing an error.
Call: !subzone [TITLE] { BLOCK } Call: !subzone [TITLE] { BLOCK }
@ -972,6 +1040,10 @@ Old usage: !subzone graphics {
Now use: !zone graphics { Now use: !zone graphics {
!source "graphics.a" !source "graphics.a"
} }
If you just want to assemble an old source code
without touching it, use the "--dialect" CLI switch:
Using "--dialect 0.94.6" or earlier will assemble this
pseudo opcode without throwing an error.
Call: !realpc Call: !realpc
@ -985,3 +1057,8 @@ Old usage: !pseudopc $0400
Now use: !pseudopc $0400 { Now use: !pseudopc $0400 {
; imagine some code here... ; imagine some code here...
} }
If you just want to assemble an old source code
without touching it, use the "--dialect" CLI switch:
Using "--dialect 0.94.6" or earlier will assemble this
pseudo opcode without throwing an error.
Using "--dialect 0.85", not even a warning is thrown.

View File

@ -38,15 +38,16 @@ Assembling buggy JMP($xxff) instruction
location ARGUMENT + 1, but from ARGUMENT - 255. Therefore ACME location ARGUMENT + 1, but from ARGUMENT - 255. Therefore ACME
issues this warning if you are about to generate such an issues this warning if you are about to generate such an
instruction. instruction.
Note that this warning is only given for CPU types 6502 and 6510, Note that this warning is only given for some CPU types (6502,
because 65c02 and 65816 have been fixed in this respect. nmos6502/6510, c64dtv2) because later ones like 65c02 and 65816
have been fixed in this regard.
Assembling unstable ANE #NONZERO instruction Assembling unstable ANE #NONZERO instruction
Assembling unstable LXA #NONZERO instruction Assembling unstable LXA #NONZERO instruction
These warnings are only ever given for CPU type 6510. ANE and LXA These warnings are only ever given for CPU type nmos6502 (6510).
are undocumented ("illegal") opcodes of this CPU, and they only ANE and LXA are undocumented ("illegal") opcodes of this CPU, and
work reliably if the argument is zero or the accumulator contains they only work reliably if the argument is zero or the accumulator
0xff. contains 0xff.
Therefore ACME issues these warnings if it is about to generate Therefore ACME issues these warnings if it is about to generate
these instructions with a non-zero argument. these instructions with a non-zero argument.
@ -64,7 +65,7 @@ C-style "==" comparison detected.
Converted to integer for binary logic operator. Converted to integer for binary logic operator.
Applying binary logic to float values does not make much sense, Applying binary logic to float values does not make much sense,
therefore floats will be converted to integer in this case. therefore floats will be converted to integer in such cases.
"EOR" is deprecated; use "XOR" instead. "EOR" is deprecated; use "XOR" instead.
This means the operator, not the mnemonic. This means the operator, not the mnemonic.
@ -72,7 +73,9 @@ Converted to integer for binary logic operator.
Found old "!for" syntax. Found old "!for" syntax.
Please update your sources to use the new "!for" syntax. See Please update your sources to use the new "!for" syntax. See
AllPOs.txt for details. AllPOs.txt for details.
You can suppress this warning using the "-Wno-old-for" switch. You can suppress this warning using the "--dialect" or the
"-Wno-old-for" CLI switch.
("-Wno-old-for" does _exactly_ the same as "--dialect 0.94.8")
Found new "!for" syntax. Found new "!for" syntax.
When using the "-Wno-old-for" switch to disable the warning about When using the "-Wno-old-for" switch to disable the warning about
@ -187,14 +190,23 @@ Section: Errors during assembly
"!cbm" is obsolete; use "!ct pet" instead. "!cbm" is obsolete; use "!ct pet" instead.
This is given when the now obsolete "!cbm" pseudo opcode is This is given when the now obsolete "!cbm" pseudo opcode is
encountered. encountered.
If you want to assemble an old source code without first updating
it, you can use the "--dialect" CLI switch to make ACME mimic an
older version.
"!pseudopc/!realpc" is obsolete; use "!pseudopc {}" instead. "!pseudopc/!realpc" is obsolete; use "!pseudopc {}" instead.
This is given when one of the now obsolete !pseudopc/!realpc This is given when one of the now obsolete !pseudopc/!realpc
pseudo opcodes is encountered. pseudo opcodes is encountered.
If you want to assemble an old source code without first updating
it, you can use the "--dialect" CLI switch to make ACME mimic an
older version.
"!subzone {}" is obsolete; use "!zone {}" instead. "!subzone {}" is obsolete; use "!zone {}" instead.
This is given when the now obsolete "!subzone" pseudo opcode is This is given when the now obsolete "!subzone" pseudo opcode is
encountered. encountered.
If you want to assemble an old source code without first updating
it, you can use the "--dialect" CLI switch to make ACME mimic an
older version.
!error: ... !error: ...
This is given when the pseudo opcode "!error" is executed. The This is given when the pseudo opcode "!error" is executed. The
@ -293,7 +305,7 @@ Negative value - cannot choose addressing mode.
your program to use positive addresses instead. your program to use positive addresses instead.
No string given. No string given.
ACME expects a string but doesn't find it. ACME expects a string but doesn't find it, or the string is empty.
Number out of range. Number out of range.
A value is too high or too low. A value is too high or too low.
@ -315,6 +327,9 @@ Quotes still open at end of line.
Source file contains illegal character. Source file contains illegal character.
Your source code file contained a null byte. Your source code file contained a null byte.
String length is not 1.
You tried something like LDA#"X" with an illegal string length.
Symbol already defined. Symbol already defined.
You defined a symbol that already had a different type or value. You defined a symbol that already had a different type or value.
To change a symbol's type or value, use the "!set" pseudo opcode. To change a symbol's type or value, use the "!set" pseudo opcode.
@ -334,8 +349,8 @@ Target out of range (N; M too far).
away, the code would assemble. away, the code would assemble.
There's more than one character. There's more than one character.
You used a text string in an arithmetic expression, but the string You used a text string containing more than one character in a
contained more than a single character. situation where only a string with length one is allowed.
Too late for postfix. Too late for postfix.
You can only postfix symbols at the start, before they are used for You can only postfix symbols at the start, before they are used for
@ -346,8 +361,8 @@ Too many '('.
Un-pseudopc operator '&' can only be applied to labels. Un-pseudopc operator '&' can only be applied to labels.
You tried to apply the operator '&' to something that is not a You tried to apply the operator '&' to something that is not a
label. This operator only works on labels, it cannot be used on label. This operator only works on labels and on '*' (the program
other objects. counter), it cannot be used on other objects.
Un-pseudopc operator '&' has no !pseudopc context. Un-pseudopc operator '&' has no !pseudopc context.
You either tried to apply the operator '&' to something that is You either tried to apply the operator '&' to something that is
@ -443,7 +458,7 @@ Too deeply nested. Recursive "!source"?
The default limit is 64, this can be changed using the The default limit is 64, this can be changed using the
"--maxdepth" CLI switch. "--maxdepth" CLI switch.
Value not yet defined. Value not defined.
A value could not be worked out. Maybe you mistyped a symbol name. A value could not be worked out. Maybe you mistyped a symbol name.
Whether this is given as a "normal" or as a serious error depends Whether this is given as a "normal" or as a serious error depends
on the currently parsed pseudo opcode. on the currently parsed pseudo opcode.
@ -489,6 +504,9 @@ ArgStackNotEmpty
The expression parser has finished though there are still The expression parser has finished though there are still
arguments left to process. arguments left to process.
ExtendingListWithItself
There were multiple references to the same list.
IllegalBlockTerminator IllegalBlockTerminator
A RAM block (macro or loop) was terminated incorrectly. A RAM block (macro or loop) was terminated incorrectly.
@ -502,6 +520,9 @@ IllegalImmediateMode
The mnemonic tree contains invalid info about the size of immediate The mnemonic tree contains invalid info about the size of immediate
arguments. arguments.
IllegalNumberTypeX
A number was neither INT nor FLOAT nor UNDEFINED.
IllegalOperatorId IllegalOperatorId
IllegalOperatorGroup IllegalOperatorGroup
The expression parser found an operator that does not exist. The expression parser found an operator that does not exist.

View File

@ -6,15 +6,15 @@
- free software - - free software -
(C) 1998-2019 Marco Baye (C) 1998-2020 Marco Baye
---------------------------------------------------------------------- ----------------------------------------------------------------------
Section: Copyright Section: Copyright
---------------------------------------------------------------------- ----------------------------------------------------------------------
ACME - a crossassembler for producing 6502/6510/65c02/65816 code. ACME - a crossassembler for producing 6502/65c02/65816 code.
Copyright (C) 1998-2017 Marco Baye Copyright (C) 1998-2020 Marco Baye
The ACME icon was designed by Wanja "Brix" Gayk The ACME icon was designed by Wanja "Brix" Gayk
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -39,9 +39,9 @@ Section: Introduction
ACME is a crossassembler for the 65xx range of processors. It knows ACME is a crossassembler for the 65xx range of processors. It knows
about the standard 6502, the 65c02 and the 65816. It also supports about the standard 6502, the 65c02 and the 65816. It also supports
the undocumented ("illegal") opcodes of the 6510 processor (a 6502- the undocumented ("illegal") opcodes of the NMOS versions of the 6502,
variant that is used in the Commodore C=64), and the extensions added like the 6510 variant that is used in the Commodore C=64, and it also
in the C64DTV2. supports extensions to the intruction set done by other parties.
This text and the other files in the same directory only describe the This text and the other files in the same directory only describe the
basic functions independent of the platform used. There should be basic functions independent of the platform used. There should be
@ -61,14 +61,16 @@ The files in the docs directory and what they contain:
Help.txt ...is this text. Help.txt ...is this text.
Illegals.txt Support for undocumented opcodes Illegals.txt Support for undocumented opcodes
Lib.txt Information about the library Lib.txt Information about the library
QuickRef.txt All the basic stuff about ACME QuickRef.txt All the basic stuff about ACME <- START HERE!
Source.txt How to compile ACME Source.txt How to compile ACME
Upgrade.txt Incompatibilities to earlier versions Upgrade.txt Incompatibilities to earlier versions
cputypes/ Instruction sets of target CPUs cputypes/ Instruction sets of target CPUs
IMPORTANT: If you upgrade from ACME 0.05 or earlier, don't forget to IMPORTANT: If you upgrade from an earlier version of ACME, don't
read the file "Upgrade.txt" - release 0.07 and all later ones are forget to read the files "Changes.txt" and "Upgrade.txt". Adding new
slightly incompatible to 0.05 and earlier. features can not always be done in a 100% compatible way, so newer
version may behave slightly different. To solve this problem, the
"--dialect" CLI switch can be used.
If you want to start using ACME right away, read the file If you want to start using ACME right away, read the file
"QuickRef.txt", it contains the main help text. "QuickRef.txt", it contains the main help text.
@ -79,15 +81,14 @@ Section: What it can and does
---------------------------------------------------------------------- ----------------------------------------------------------------------
ACME is a crossassembler. ACME is a crossassembler.
ACME can produce code for the 6502, 6510, 65c02 and 65816 processors. ACME can produce code for the 6502, 65c02 and 65816 processors.
It does this *fast*. It does this *fast*.
It can produce at most 64 KBytes of code. It can produce at most 64 KBytes of code.
You can use global labels, local labels and anonymous labels. You can use global labels, local labels and anonymous labels.
It is fast. It is fast.
You can use global and local macros. You can use global and local macros.
You can use conditional assembly. You can use conditional assembly.
You can use looping assembly (There are two ways to do this; a very You can use looping assembly.
simple and a very flexible one).
You can include other source files. You can include other source files.
You can include binary files (either whole or parts) directly into the You can include binary files (either whole or parts) directly into the
output. output.
@ -100,6 +101,7 @@ ACME's maths parser has no problems concerning parentheses and
indirect addressing modes. indirect addressing modes.
ACME's maths parser knows a shit load of different operations. ACME's maths parser knows a shit load of different operations.
ACME supports both integer and floating point maths operations. ACME supports both integer and floating point maths operations.
In addition to numbers, symbols can also hold strings or lists.
You can dump the global symbols into a file. You can dump the global symbols into a file.
ACME supports a library of commonly used macros and symbols. ACME supports a library of commonly used macros and symbols.
It always takes as many passes as are needed. It always takes as many passes as are needed.

View File

@ -8,7 +8,7 @@
In release 0.87, support for some of the undocumented opcodes of the In release 0.87, support for some of the undocumented opcodes of the
6502 processor was added. NMOS 6502 processor was added.
In release 0.89, some more were added. In release 0.89, some more were added.
In release 0.94.8, another one was added (lxa). In release 0.94.8, another one was added (lxa).
In release 0.95.3, C64DTV2 support was added, which includes these In release 0.95.3, C64DTV2 support was added, which includes these
@ -31,7 +31,6 @@ opcodes (mnemonics in parentheses are used by other sources):
dcp (dcm) | c7 d7 cf df db c3 d3 | dec + cmp dcp (dcm) | c7 d7 cf df db c3 d3 | dec + cmp
isc (isb, ins) | e7 f7 ef ff fb e3 f3 | inc + sbc isc (isb, ins) | e7 f7 ef ff fb e3 f3 | inc + sbc
las (lar, lae) | bb | A,X,S = {addr} & S las (lar, lae) | bb | A,X,S = {addr} & S
These five are said to be unstable:
tas (shs, xas) | 9b | S = A & X {addr} = A&X& {H+1} tas (shs, xas) | 9b | S = A & X {addr} = A&X& {H+1}
sha (axa, ahx) | 9f 93 | {addr} = A & X & {H+1} sha (axa, ahx) | 9f 93 | {addr} = A & X & {H+1}
shx (xas, sxa) | 9e | {addr} = X & {H+1} shx (xas, sxa) | 9e | {addr} = X & {H+1}
@ -53,13 +52,15 @@ These two are somewhat unstable, because they involve an arbitrary value:
lxa (lax, atx) | ab*** | A,X = (A | ??) & arg lxa (lax, atx) | ab*** | A,X = (A | ??) & arg
Example: Example:
!cpu 6510 ; activate additional mnemonics... !cpu nmos6502 ; activate additional mnemonics...
lax (some_zp_label,x) ; ...and use them. No, this lax (some_zp_label,x) ; ...and use them. No, this
dcp (other_zp_label),y ; example does not make sense. dcp (other_zp_label),y ; example does not make sense.
*) Up until ACME version 0.95.1, anc#8 generated opcode 0x2b. Since *) Up until ACME version 0.95.1, anc#8 generated opcode 0x2b. Since
ACME version 0.95.2, anc#8 generates opcode 0x0b. Both opcodes work ACME version 0.95.2, anc#8 generates opcode 0x0b. Both opcodes work
the same way on a real 6510 CPU, but they do not work on the C64DTV2. the same way on a real NMOS 6502 CPU, but they do not work on the
C64DTV2.
Using the "--dialect" CLI switch does not change the generated opcode!
**) Note that DOP ("double nop") and TOP ("triple nop") can be used **) Note that DOP ("double nop") and TOP ("triple nop") can be used
with implied addressing, but the generated opcodes are those for with implied addressing, but the generated opcodes are those for
@ -68,7 +69,7 @@ and TOP can be used to skip the following one- or two-byte
instruction. instruction.
Using DOP/TOP with x-indexed addressing might have its uses when Using DOP/TOP with x-indexed addressing might have its uses when
timing is critical (crossing a page border adds a penalty cycle). timing is critical (crossing a page border adds a penalty cycle).
Unless using implied addressing, DOP/TOP can now also be written as NOP. Unless using implied addressing, DOP/TOP can also be written as NOP.
***) ANE and LXA first perform an ORA with an arbitrary(!) value and ***) ANE and LXA first perform an ORA with an arbitrary(!) value and
then perform an AND with the given argument. So they are unstable and then perform an AND with the given argument. So they are unstable and
@ -79,10 +80,8 @@ ACME will output a warning if these opcodes get assembled with a
nonzero argument. nonzero argument.
There is no guarantee that these opcodes actually work on a given 6502 There is no guarantee that these opcodes actually work on a given 6502
(or 6510, or 8500, or 8502) CPU. But as far as I know, nobody ever (or 6510, or 8500, or 8501, or 8502) CPU. But as far as I know, nobody
found an unmodified C64/C128 where these illegals didn't work. That's ever found an unmodified C64/C128 where these illegals didn't work.
why I used "6510" as the CPU keyword instead of "6502illegal" or
something like that.
These illegals will definitely *not* work on 65c02 and 65816 CPUs. But These illegals will definitely *not* work on 65c02 and 65816 CPUs. But
I really should not have to tell you that ;) I really should not have to tell you that ;)
@ -92,7 +91,7 @@ people use different names for them. I hope my choices are not too
exotic for your taste. exotic for your taste.
Just for the sake of completeness: Here are all the remaining opcodes Just for the sake of completeness: Here are all the remaining opcodes
(the ones ACME won't generate even with "6510" cpu): (the ones ACME won't generate even with "nmos6502" cpu chosen):
Opcode| Description C64DTV2 Opcode| Description C64DTV2
------+-------------------------------------------------------------- ------+--------------------------------------------------------------
@ -137,7 +136,10 @@ For more information about what these opcodes do, see these documents:
Extra Instructions Of The 65XX Series CPU, Adam Vardy, 27 Sept. 1996 Extra Instructions Of The 65XX Series CPU, Adam Vardy, 27 Sept. 1996
6502 Undocumented Opcodes, by Freddy Offenga, 5/17/1997 6502 Undocumented Opcodes, by Freddy Offenga, 5/17/1997
AAY64 (All About Your 64) AAY64 (All About Your 64)
and the most comprehensive work is:
"No More Secrets - NMOS 6510 Unintended Opcodes" ...but the most comprehensive work is:
Download it from https://csdb.dk/release/?id=185341
"No More Secrets - NMOS 6510 Unintended Opcodes"
Download it from https://csdb.dk/release/?id=185341
or ask google for the latest version. or ask google for the latest version.

View File

@ -106,6 +106,10 @@ Then there are local symbols (their names starting with a '.'
character). These can only be accessed from inside the macro or zone character). These can only be accessed from inside the macro or zone
they were defined in (for more about macros and zones, see the file they were defined in (for more about macros and zones, see the file
"AllPOs.txt"). "AllPOs.txt").
There are also "cheap locals": their names start with a '@' character.
The area where these can be accessed is limited automatically by the
previous and the following global label (cheap locals are "cheap"
because you don't have to put in any extra work to limit their range).
And then there are anonymous labels (their names being sequences of And then there are anonymous labels (their names being sequences of
either '-' or '+' characters). They are also local (bound to their either '-' or '+' characters). They are also local (bound to their
macro/zone), but in addition to that, the "-" labels can only be used macro/zone), but in addition to that, the "-" labels can only be used
@ -113,6 +117,7 @@ for backward references, while the "+" labels can only be used for
forward references. forward references.
In contrast to global and local labels, anonymous labels can not be In contrast to global and local labels, anonymous labels can not be
defined explicitly (as in SYMBOL = VALUE). defined explicitly (as in SYMBOL = VALUE).
Each macro call automatically gets its own scope for local symbols.
Save the given example source code to a file called "tiny.a" and start Save the given example source code to a file called "tiny.a" and start
acme by typing acme by typing
@ -149,7 +154,7 @@ found in the file "AllPOs.txt". Here's just a short overview:
!convtab !pet !raw !scr !scrxor !text !convtab !pet !raw !scr !scrxor !text
...for converting and outputting strings. ...for converting and outputting strings.
!do !endoffile !for !if !ifdef !ifndef !set !do !endoffile !for !if !ifdef !ifndef !set !while
...for flow control; looping assembly and conditional assembly. ...for flow control; looping assembly and conditional assembly.
!binary !source !to !binary !source !to
@ -170,7 +175,7 @@ found in the file "AllPOs.txt". Here's just a short overview:
!warn !error !serious !warn !error !serious
...for generating warnings, errors and serious errors. ...for generating warnings, errors and serious errors.
!addr !address
...to mark symbols as addresses, for the optional type check system. ...to mark symbols as addresses, for the optional type check system.
@ -276,6 +281,8 @@ Available options are:
-Wno-old-for -Wno-old-for
Disables warnings about the old "!for" syntax and at the Disables warnings about the old "!for" syntax and at the
same time enables warnings about the _new_ "!for" syntax. same time enables warnings about the _new_ "!for" syntax.
Internally, this does exactly the same as what happens
when the "--dialect 0.94.8" CLI switch is used...
-Wtype-mismatch -Wtype-mismatch
Enables type checking system (warns about wrong types). Enables type checking system (warns about wrong types).
@ -296,6 +303,15 @@ Available options are:
from '!' to '.' (so sources intended for other assemblers can from '!' to '.' (so sources intended for other assemblers can
be converted with less effort). be converted with less effort).
--dialect VERSION behave like different version
This CLI switch tells ACME to mimic the behavior of an older
version. Use this with a bogus version to get a list of all
supported ones.
--test enable experimental features
This is for people who want to help test new features before
they are officially announced.
-V, --version show version and exit. -V, --version show version and exit.
Platform-specific versions of ACME might offer more options. Platform-specific versions of ACME might offer more options.
@ -389,8 +405,8 @@ $d011 hexadecimal values are indicated by either a
more readable, especially when building bitmapped more readable, especially when building bitmapped
objects (like C64 sprites or fonts) in your source objects (like C64 sprites or fonts) in your source
code. code.
"p" character values are indicated by double or single 'p' character values are indicated by single or double
'q' quotes. The actual numeric value depends on the "q" quotes. The actual numeric value depends on the
current conversion table (none/petscii/screen), current conversion table (none/petscii/screen),
chosen using the "!ct" pseudo opcode. chosen using the "!ct" pseudo opcode.
poll_joy2 a global symbol poll_joy2 a global symbol

View File

@ -695,7 +695,7 @@ static void list_append_list(struct listitem *selfhead, struct listitem *otherhe
struct listitem *item; struct listitem *item;
if (selfhead == otherhead) if (selfhead == otherhead)
Bug_found("ExtendingListWithItself", 0); // TODO - add to docs! Bug_found("ExtendingListWithItself", 0);
item = otherhead->next; item = otherhead->next;
while (item != otherhead) { while (item != otherhead) {
list_append_object(selfhead, &item->u.payload); list_append_object(selfhead, &item->u.payload);
@ -1659,7 +1659,7 @@ static void number_handle_monadic_operator(struct object *self, const struct op
float_handle_monadic_operator(self, op); float_handle_monadic_operator(self, op);
break; break;
default: default:
Bug_found("IllegalNumberType1", self->u.number.ntype); // FIXME - add to docs! Bug_found("IllegalNumberType1", self->u.number.ntype);
} }
} }
@ -2101,7 +2101,7 @@ static void number_handle_dyadic_operator(struct object *self, const struct op *
else if (self->u.number.ntype == NUMTYPE_FLOAT) else if (self->u.number.ntype == NUMTYPE_FLOAT)
float_handle_dyadic_operator(self, op, other); float_handle_dyadic_operator(self, op, other);
else else
Bug_found("IllegalNumberType2", self->u.number.ntype); // FIXME - add to docs! Bug_found("IllegalNumberType2", self->u.number.ntype);
} }
@ -2122,7 +2122,7 @@ static int get_valid_index(int *target, int length, const struct object *self, c
if (other->u.number.ntype == NUMTYPE_FLOAT) if (other->u.number.ntype == NUMTYPE_FLOAT)
float_to_int(other); float_to_int(other);
if (other->u.number.ntype != NUMTYPE_INT) if (other->u.number.ntype != NUMTYPE_INT)
Bug_found("IllegalNumberType3", other->u.number.ntype); // FIXME - add to docs! Bug_found("IllegalNumberType3", other->u.number.ntype);
index = other->u.number.val.intval; index = other->u.number.val.intval;
// negative indices access from the end // negative indices access from the end
@ -2279,7 +2279,7 @@ static void number_fix_result(struct object *self)
&& (self->u.number.val.fpval >= -128.0)) && (self->u.number.val.fpval >= -128.0))
self->u.number.flags |= NUMBER_FITS_BYTE; // FIXME - what for? isn't this flag only of use when undefined? self->u.number.flags |= NUMBER_FITS_BYTE; // FIXME - what for? isn't this flag only of use when undefined?
} else { } else {
Bug_found("IllegalNumberType4", self->u.number.ntype); // FIXME - add to docs! Bug_found("IllegalNumberType4", self->u.number.ntype);
} }
} }
@ -2307,7 +2307,7 @@ static void number_print(const struct object *self, struct dynabuf *db)
sprintf(buffer, "%.30g", self->u.number.val.fpval); sprintf(buffer, "%.30g", self->u.number.val.fpval);
DynaBuf_add_string(db, buffer); DynaBuf_add_string(db, buffer);
} else { } else {
Bug_found("IllegalNumberType5", self->u.number.ntype); // FIXME - add to docs! Bug_found("IllegalNumberType5", self->u.number.ntype);
} }
} }
@ -2480,7 +2480,7 @@ void ALU_any_int(intval_t *target) // ACCEPT_UNDEFINED
else if (expression.result.u.number.ntype == NUMTYPE_FLOAT) else if (expression.result.u.number.ntype == NUMTYPE_FLOAT)
*target = expression.result.u.number.val.fpval; *target = expression.result.u.number.val.fpval;
else else
Bug_found("IllegalNumberType6", expression.result.u.number.ntype); // FIXME - add to docs! Bug_found("IllegalNumberType6", expression.result.u.number.ntype);
} else { } else {
*target = 0; *target = 0;
Throw_error(exception_not_number); Throw_error(exception_not_number);
@ -2510,13 +2510,13 @@ void ALU_defined_int(struct number *intresult) // no ACCEPT constants?
Throw_serious_error(exception_no_value); Throw_serious_error(exception_no_value);
if (expression.result.type == &type_number) { if (expression.result.type == &type_number) {
if (expression.result.u.number.ntype == NUMTYPE_UNDEFINED) { if (expression.result.u.number.ntype == NUMTYPE_UNDEFINED) {
Throw_serious_error(exception_value_not_defined); Throw_serious_error("Value not defined.");
} else if (expression.result.u.number.ntype == NUMTYPE_INT) { } else if (expression.result.u.number.ntype == NUMTYPE_INT) {
// ok // ok
} else if (expression.result.u.number.ntype == NUMTYPE_FLOAT) { } else if (expression.result.u.number.ntype == NUMTYPE_FLOAT) {
float_to_int(&expression.result); float_to_int(&expression.result);
} else { } else {
Bug_found("IllegalNumberType7", expression.result.u.number.ntype); // FIXME - add to docs! Bug_found("IllegalNumberType7", expression.result.u.number.ntype);
} }
} else { } else {
Throw_serious_error(exception_not_number); Throw_serious_error(exception_not_number);
@ -2548,7 +2548,7 @@ void ALU_addrmode_int(struct expression *expression, int paren) // ACCEPT_UNDEFI
// FIXME - throw a warning? // FIXME - throw a warning?
string_to_byte(&(expression->result), 0); string_to_byte(&(expression->result), 0);
} else { } else {
Throw_error("String length is not 1."); // FIXME - add to docs! Throw_error("String length is not 1.");
} }
} else { } else {
Throw_error(exception_not_number); Throw_error(exception_not_number);

View File

@ -57,7 +57,6 @@ const char exception_number_out_of_range[] = "Number out of range.";
const char exception_pc_undefined[] = "Program counter undefined."; const char exception_pc_undefined[] = "Program counter undefined.";
const char exception_symbol_defined[] = "Symbol already defined."; const char exception_symbol_defined[] = "Symbol already defined.";
const char exception_syntax[] = "Syntax error."; const char exception_syntax[] = "Syntax error.";
const char exception_value_not_defined[] = "Value not defined.";
// default value for number of errors before exiting // default value for number of errors before exiting
#define MAXERRORS 10 #define MAXERRORS 10

View File

@ -46,7 +46,6 @@ extern const char exception_number_out_of_range[];
extern const char exception_pc_undefined[]; extern const char exception_pc_undefined[];
extern const char exception_symbol_defined[]; extern const char exception_symbol_defined[];
extern const char exception_syntax[]; extern const char exception_syntax[];
extern const char exception_value_not_defined[];
// byte flags table // byte flags table
extern const char global_byte_flags[]; extern const char global_byte_flags[];
#define BYTE_STARTS_KEYWORD(b) (global_byte_flags[(unsigned char) b] & (1u << 7)) // byte is allowed at start of keyword (a-z, A-Z, _, everything>127) #define BYTE_STARTS_KEYWORD(b) (global_byte_flags[(unsigned char) b] & (1u << 7)) // byte is allowed at start of keyword (a-z, A-Z, _, everything>127)