Added BYTE REGISTER to docs and to editor syntaxes

This commit is contained in:
Mattias Hansson 2026-07-14 19:20:27 +02:00
parent 501f58a968
commit b49ba67aac
5 changed files with 76 additions and 5 deletions

View file

@ -113,12 +113,14 @@ WEND
## BYTE ## BYTE
Declares an 8-bit variable or constant. Declares an 8-bit variable, register-hinted temporary, or constant.
**Syntax:** **Syntax:**
``` ```
BYTE <varname> BYTE <varname>
BYTE <varname> = <value> BYTE <varname> = <value>
BYTE REGISTER <varname>
BYTE REGISTER <varname> = <value>
BYTE <varname> @ <address> BYTE <varname> @ <address>
BYTE CONST <varname> = <value> BYTE CONST <varname> = <value>
``` ```
@ -127,10 +129,17 @@ BYTE CONST <varname> = <value>
``` ```
BYTE counter BYTE counter
BYTE speed = 5 BYTE speed = 5
BYTE REGISTER temp
BYTE REGISTER scratch = 0
BYTE screen @ $D020 BYTE screen @ $D020
BYTE CONST MAX_SPEED = 10 BYTE CONST MAX_SPEED = 10
``` ```
`REGISTER` is a storage hint for the optimizer. The variable may be kept in a
CPU register (A/X/Y) and its memory allocation can be eliminated entirely.
Only valid inside `FUNC`/`FEND` blocks; incompatible with `@` and `CONST`.
See the REGISTER section in `language.md` for details.
--- ---
## CALL ## CALL

View file

@ -43,6 +43,7 @@
<item>POINTER</item> <item>POINTER</item>
<item>POKE</item> <item>POKE</item>
<item>POKEW</item> <item>POKEW</item>
<item>REGISTER</item>
<item>SCRIPT</item> <item>SCRIPT</item>
<item>STEP</item> <item>STEP</item>
<item>SUBEND</item> <item>SUBEND</item>

View file

@ -38,7 +38,7 @@ contexts:
scope: meta.preprocessor.c65cm scope: meta.preprocessor.c65cm
keywords: keywords:
- match: '\b(ADD|AND|AS|ASM|BREAK|BYTE|CALL|CASE|CONST|DEC|DECREMENT|DEFAULT|DO|ELSE|ENDASM|ENDIF|ENDSCRIPT|ENDSWITCH|EXIT|FEND|FOR|FUNC|GOSUB|GOTO|IF|INC|INCREMENT|LABEL|LET|LIBRARY|MACRO|NEXT|OR|ORIGIN|PASSING|PEEK|PEEKW|POINTER|POKE|POKEW|SCRIPT|STEP|SUBEND|SUBTRACT|SWITCH|THEN|TO|WHILE|WITH|WEND|WORD|XOR)\b' - match: '\b(ADD|AND|AS|ASM|BREAK|BYTE|CALL|CASE|CONST|DEC|DECREMENT|DEFAULT|DO|ELSE|ENDASM|ENDIF|ENDSCRIPT|ENDSWITCH|EXIT|FEND|FOR|FUNC|GOSUB|GOTO|IF|INC|INCREMENT|LABEL|LET|LIBRARY|MACRO|NEXT|OR|ORIGIN|PASSING|PEEK|PEEKW|POINTER|POKE|POKEW|REGISTER|SCRIPT|STEP|SUBEND|SUBTRACT|SWITCH|THEN|TO|WHILE|WITH|WEND|WORD|XOR)\b'
scope: keyword.control.c65cm scope: keyword.control.c65cm
- match: '\b(in|out|io)\b(?=\s*:)' - match: '\b(in|out|io)\b(?=\s*:)'
scope: storage.modifier.parameter.c65cm scope: storage.modifier.parameter.c65cm

View file

@ -732,3 +732,36 @@ func TestPassRegDead_CallSiteKeepsStore(t *testing.T) {
t.Errorf("expected sta call_val to be kept (before jsr), got:\n%s", joined) t.Errorf("expected sta call_val to be kept (before jsr), got:\n%s", joined)
} }
} }
func TestPassRegDead_InitValueFlowsThrough(t *testing.T) {
// BYTE REGISTER x = 42; POKE $d020, x → value flows through A, never touches RAM
input := []string{
"\tlda #$2a",
"\tsta test_x",
"\tlda test_x",
"\tsta 53280",
"\trts",
}
cfg := &Config{
EnableRegisterVars: true,
EnableStoreLoad: true,
RegisterVars: map[string]bool{
"test_x": true,
},
}
output, dissolved := Optimize(input, cfg)
if !dissolved["test_x"] {
t.Error("test_x should be dissolved — init value flows through A to POKE")
}
joined := strings.Join(output, "\n")
if strings.Contains(joined, "test_x") {
t.Errorf("expected no reference to test_x in output, got:\n%s", joined)
}
if !strings.Contains(joined, "lda #$2a") {
t.Error("init value lda #$2a should survive")
}
}

View file

@ -88,12 +88,40 @@ initialize()
BYTE variables store 8-bit values (0-255). BYTE variables store 8-bit values (0-255).
```c65 ```c65
BYTE count // Uninitialized BYTE count // Uninitialized (0)
BYTE speed = 5 // Initialized to 5 BYTE speed = 5 // Initialized to 5
BYTE REGISTER temp // Register-hinted (default 0)
BYTE REGISTER scratch = 0 // Register-hinted with init value
BYTE screen @ $D020 // Memory-mapped to specific address BYTE screen @ $D020 // Memory-mapped to specific address
BYTE CONST MAX_SPEED = 10 // Constant (recommended over #DEFINE) BYTE CONST MAX_SPEED = 10 // Constant
``` ```
### REGISTER Variables
The `REGISTER` hint tells the optimizer that a BYTE variable's value only matters as
it flows through the computation — never its stored location. The compiler can keep
the value in a CPU register (A, X, or Y) and eliminate dead stores and memory
allocations entirely.
```c65
FUNC fast_copy
BYTE REGISTER b
b = PEEK $d011
b = b | 32
POKE $d011, b // b never touches RAM — flows through A
FEND
```
**Rules:**
- `BYTE REGISTER` is only valid inside `FUNC`/`FEND` blocks (function-local only)
- Cannot be combined with `@` (memory-mapped) or `CONST`
- Cannot be referenced from `ASM`, `SCRIPT`, or `MACRO` blocks via `|varname|`
- `WORD REGISTER` is not supported (the 6502 has no 16-bit ALU register)
- Without `--opt`, `REGISTER` behaves identically to a normal `BYTE`
- With `--opt`, the optimizer may dissolve the variable, eliminating its `!8`
allocation and all store/load operations
### WORD Variables ### WORD Variables
WORD variables store 16-bit values (0-65535). WORD variables store 16-bit values (0-65535).
@ -109,7 +137,7 @@ WORD CONST SCREEN_RAM = $0400 // Constant
### Memory-Mapped Variables ### Memory-Mapped Variables
Variables can be placed at specific addresses using `@`: Variables can be placed at specific addresses using `@`. Not compatible with `REGISTER`.
```c65 ```c65
BYTE borderColor @ $D020 // VIC-II border color BYTE borderColor @ $D020 // VIC-II border color