[Research] Assigning near functions from other groups to near function pointers

Yup, P0148 didn't actually solve the issue it was meant to solve, and
I still had to research this workaround.

Part of P0149, funded by Blue Bolt, Ember2528, and -Tom-.
This commit is contained in:
nmlgc 2021-07-21 22:14:38 +02:00
parent 3e658b590d
commit 6233a3c3e3
2 changed files with 44 additions and 1 deletions

View File

@ -205,6 +205,40 @@ case it's part of an arithmetic expression that was promoted to `int`.
However, this only generates identical code to the original optimization if
passing the `other` parameter can be inlined, which isn't always the case.
## Function pointers
Type syntax (cf. [platform.h](../platform.h)):
| | … near function | … far function |
|------------------|---------------------------|--------------------------|
| Near pointer to… | `int (near *near nn_t)()` | `int (far *near fn_t)()` |
| Far pointer to… | `int (near *far  nf_t)()` | `int (far *far  ff_t)()` |
Calling conventions can be added before the `*`.
* Assigning a `near` function defined in a different group to a `nn_t` will
cause a fixup overflow error at link time. The reason for that is pointed
out in the compiler's assembly output (`-S)`:
| C | ASM |
|--------------------------|----------------------------------------------------------------------------------------|
| `void near bar();` | `extrn _bar:near` |
| `static nn_t foo = bar;` | `mov word ptr DGROUP:_popup, offset CURRENTLY_COMPILED_BUT_NOT_ACTUAL_GROUP_OF:_bar` |
The only known way of enforcing this assignment involves declaring `bar()` as
a far function and then casting its implicit segment away:
```c++
void far bar();
static nn_t foo = reinterpret_cast<nn_t>(bar);
```
This wrong declaration of `bar()` must, of course, not be `#include`d into
the translation unit that actually defines `bar()` as a `near` function, as
it was intended. It can't also be local to an inlined function that's part of
a public header, since those declarations seem to escape to the global scope
there.
## `switch` statements
* Sequence of the individual cases is identical in both C and ASM

View File

@ -16,7 +16,16 @@ extern nearfunc_t_near overlay_text;
extern nearfunc_t_near popup;
extern unsigned long popup_bonus;
void pascal near popup_update_and_render(void);
#define popup_show(popup_new) \
/* Yup, the only known way of assigning a `near` function to a `near` */ \
/* function pointer from a group outside the one where the `near` */ \
/* function was declared in involves lying to the compiler about the */ \
/* true distance of the function. That's also why we can't correctly */ \
/* declare this function at global scope. */ \
void pascal far popup_update_and_render(void); \
\
popup_id_new = popup_new; \
popup = reinterpret_cast<nearfunc_t_near>(popup_update_and_render);
/// Stage and BGM titles
/// --------------------