1
0
mirror of https://github.com/KarolS/millfork.git synced 2026-04-20 18:16:35 +00:00

Changes to macros and parameter list syntax:

* non-asm macros can now take `const` and `call` parameters
* register parameters to asm functions and macros can be given names if annotated explicitly
This commit is contained in:
Karol Stasiak
2020-03-30 19:23:48 +02:00
parent 5cdc599b1d
commit 63ff28e94e
50 changed files with 465 additions and 191 deletions
+40 -5
View File
@@ -18,9 +18,20 @@ It implies the following:
* their invocations cannot be used as expressions
* in case of `asm` macros, the parameters must be defined as either `const` (compile-time constants) or `ref` (variables)
* in case of `asm` macros, the parameters:
* in case of non-`asm` macros, the parameters must be variables; exceptionally, their type may be declared as `void`
* must be defined as either `const` (compile-time constants), `ref` (variables) or `register(XX)` (registers, where XX is the register you want to use)
* at most one parameter can be defined as a register
* in case of non-`asm` macros, the parameters
* must be defined as either `ref` (variables; default, may be omitted) `const` (compile-time constants), or `call` (expressions, which are evaluated every time they are used)
* `ref` parameters exceptionally can have their type declared as `void`; such parameters accept variables of any type
* `call` parameters exceptionally can have their type declared as `void`;
such parameters accept expressions of any type, including `void`, however, you cannot assign from those expressions
* macros do not have their own scope (they reuse the scope from their invocations) exceptions: the parameters and the local labels defined in assembly
@@ -28,9 +39,12 @@ It implies the following:
When invoking a macro, you need to pass variables as arguments to parameters annotated with `ref` and constants as arguments annotated with `const`.
Invoking a non-`asm` macro requires the types of passed variables to match precisely. No type conversions are performed.
Invoking a non-`asm` macro requires the types of variables via `ref` parameters to match precisely.
No type conversions are performed.
Exception: parameters of type `void` can accept a variable of any type.
For parameters defined as `const`, `register(XX)` or `call`, the usual type conversions are performed.
You can invoke a macro from assembly, by preceding the invocation with `+`
Examples:
@@ -52,6 +66,14 @@ Examples:
inc(x)
return x
}
macro void perform_twice(void call f) {
f
f
}
byte add_two_3(byte x) {
perform_twice(inc(x))
return x
}
macro void add(byte b, byte v) {
b += v
@@ -59,7 +81,7 @@ Examples:
macro void retu(byte result) {
return result
}
byte add_two_3(byte x) {
byte add_two_4(byte x) {
add(x, 2)
retu(x)
}
@@ -68,13 +90,26 @@ Examples:
LDA b
CLC
ADC #v
STA b
// no RTS!
}
byte add_two_4(byte x) {
byte add_two_5(byte x) {
add_asm(x, 2)
return x
}
macro asm byte add_asm_2(byte ref b, byte register(x) v) {
TXA
CLC
ADC b
STA b
// no RTS!
}
byte add_two_6(byte x) {
add_asm_2(x, 2)
return x
}
## Automatic inlining
+4 -2
View File
@@ -151,9 +151,11 @@ Non-macro functions can only have their parameters passed via registers:
* `word xa`, `word ax`, `word ay`, `word ya`, `word xy`, `word yx`: a 2-byte word byte passed via given two CPU registers,
with the high byte passed through the first register and the low byte passed through the second register; any 2-byte type can be used
* the above, but written more explicitly: `byte register(a) paramname`, `byte register(x) paramname`, `word register(ax) paramname` etc.
For example, this piece of code:
asm void f(word ax) @F_ADDR extern
asm void f(word register(ax) value) @F_ADDR extern
f(5)
@@ -179,7 +181,7 @@ Macro assembly functions can have maximum one parameter passed via a register.
An external function should be declared with a defined memory address
and the `extern` keyword instead of the body:
asm void putchar(byte a) @$FFD2 extern
asm void putchar(byte register(a) char) @$FFD2 extern
## Safe assembly
+2
View File
@@ -120,6 +120,8 @@ Non-macro functions can only have their parameters passed via registers:
* `word d`, `word x`, `word y`: a 2-byte word byte passed via given 16-bit register; any 2-byte type can be used
* the above, but written more explicitly: `byte register(a) paramname`, `byte register(b) paramname`, `word register(x) paramname` etc.
Parameters passed via other registers (`U`, `S` etc.) or combinations of registers do not work yet.
**Work in progress**:
+2
View File
@@ -160,6 +160,8 @@ Non-macro functions can only have their parameters passed via registers:
* `word hl`, `word bc`, `word de`: a 2-byte word byte passed via given 16-bit register; any 2-byte type can be used
* the above, but written more explicitly: `byte register(a) paramname`, `byte register(b) paramname`, `word register(hl) paramname` etc.
Parameters passed via other registers (`I`, `IX`, `IY`, `IXH` etc.) or combinations of registers do not work yet.
**Work in progress**:
+1 -1
View File
@@ -14,7 +14,7 @@ Examples:
void do_nothing() { }
inline byte two() = 2
extern asm void chkout(byte a) @ $FFD2
extern asm void chkout(byte register(a) char) @ $FFD2
segment(prgrom0) void main_loop(word w, byte x) align(fast) { // body omitted
+3 -3
View File
@@ -7,10 +7,10 @@
To call an external function, you need to declare it as `asm extern`. For example:
```
asm void putchar(byte a) @$FFD2 extern
asm void putchar(byte register(a) char) @$FFD2 extern
```
The function parameter will be passed via the accumulator,
In this example, the function parameter will be passed via the accumulator,
the function itself is located in ROM at $FFD2. A call like this:
```
@@ -36,7 +36,7 @@ To call a function that has its address calculated dynamically,
you just need to do the same as what you would do in assembly; 6502 example:
```
asm void call_function(byte a) {
asm void call_function(byte register(a) param) {
JMP (function_address)
}
```
+7 -7
View File
@@ -41,7 +41,7 @@ Get joypad2's state as a byte.
Simulates a hardware reset by jumping to the reset vector,
which then calls main().
#### `void ppu_set_addr(word ax)`
#### `void ppu_set_addr(word register(ax) address)`
Sets the PPU to point at the VRAM address at ax, usually in preparation
for a write via ppu_write_data().
@@ -50,18 +50,18 @@ for a write via ppu_write_data().
Gets the PPU status byte.
#### `void ppu_set_scroll(byte a, byte x)`
#### `void ppu_set_scroll(byte register(a) xscroll, byte register(x) yscroll)`
Sets the PPU scroll register. Parameter a defines the horizontal
(X-axis) scroll value, and parameter x defines the vertical (Y-axis)
scroll value.
#### `void ppu_write_data(byte a)`
#### `void ppu_write_data(byte register(a) data)`
Writes a byte to the PPU's VRAM at the address the PPU
is currently pointing to. Usually used after a call to ppu_set_addr().
#### `void ppu_oam_dma_write(byte a)`
#### `void ppu_oam_dma_write(byte register(a) page)`
Initiates a DMA transfer of 256 bytes from CPU memory address $xx00-$xxFF
to PPU OAM memory, where xx is the hexadecimal representation of parameter a.
@@ -71,18 +71,18 @@ to PPU OAM memory, where xx is the hexadecimal representation of parameter a.
The `nes_mmc4` module is imported automatically on the NES MMC4 target
and contains routines related to MMC4 bankswitching.
#### `void set_prg_bank(byte a)`
#### `void set_prg_bank(byte register(a) bank)`
Changes the $8000-$BFFF PRG bank.
#### `void set_chr_bank0(byte a)`
#### `void set_chr_bank0(byte register(a) bank)`
Changes the CHR bank 0 ($0000-$0fff in the PPU memory space).
The high nibble (0 or 1) selects between `chrrom0` and `chrrom1` segments.
The low nibble L (0-$F) selects a 4K-aligned address in the segment ($L000).
#### `void set_chr_bank1(byte a)`
#### `void set_chr_bank1(byte register(a) bank)`
Changes the CHR bank 1 ($1000-$1fff in the PPU memory space).
+3 -3
View File
@@ -4,7 +4,7 @@
The `stdlib` module is automatically imported on most targets.
#### `macro asm void poke(word const addr, byte a)`
#### `macro asm void poke(word const addr, byte register(a) value)`
Stores a byte at given constant address. Will not be optimized away by the optimizer.
@@ -20,11 +20,11 @@ Disables interrupts.
Enables interrupts.
#### `byte hi_nibble_to_hex(byte a)`
#### `byte hi_nibble_to_hex(byte register(a) value)`
Returns an ASCII representation of the upper nibble of the given byte.
#### `byte lo_nibble_to_hex(byte a)`
#### `byte lo_nibble_to_hex(byte register(a) value)`
Returns an ASCII representation of the lower nibble of the given byte.