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:
+40
-5
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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**:
|
||||
|
||||
@@ -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**:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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).
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user