mirror of https://github.com/nmlgc/ReC98.git
56 lines
2.0 KiB
PHP
56 lines
2.0 KiB
PHP
; Reimplementation of TASM's ARG directive for those weirdly micro-optimized
|
||
; ASM functions that keep their base pointer in BX. The ENTER instruction of
|
||
; the original ARG directive is replaced with ´MOV BX, SP`. Afterwards, use
|
||
; `ret_bx` to emit the correct `RET` instruction for the given number of
|
||
; parameters.
|
||
; [distance] must be either NEAR or FAR, matching the function this macro is
|
||
; used in.
|
||
;
|
||
; [params] uses the same colon-separated `name:type` syntax as the original
|
||
; ARG directive. Since all of those functions (thankfully) use the Pascal
|
||
; calling convention, they need to be declared in reverse order, compared to
|
||
; the C declaration.
|
||
; Unfortunately, the names can't be prefixed with @@, as that would make them
|
||
; local to the macro. Hacking around this with NOLOCALS, PUSHSTATE, and
|
||
; POPSTATE would break local labels for the usage code. So, just use a single
|
||
; @ instead... or a clean namespace.
|
||
;
|
||
; MODDERS: Replace with the regular ARG and RET directives in the usage code,
|
||
; and delete this file.
|
||
arg_bx macro distance:req, params:vararg
|
||
; Expanding a text macro on the left side of `=` only seems to work in
|
||
; QUIRKS mode?!
|
||
PUSHSTATE
|
||
QUIRKS
|
||
|
||
@bp = ((-distance and 0FFFFh) * word)
|
||
@sp = @bp
|
||
for @@param, params
|
||
@@colon_pos instr <@@param>, <:>
|
||
@@name substr <@@param>, 1, (@@colon_pos - 1)
|
||
@@type substr <@@param>, (@@colon_pos + 1)
|
||
|
||
@@local_err substr <@@param>, 1, 2
|
||
errifidn @@local_err, <@@> "arg_bx: @@ in parameter names is not supported"
|
||
|
||
; % at the beginning of the line (!) forces expansion of the text
|
||
; macro @@name... in QUIRKS mode.
|
||
% @@name = @@type ptr ss:[bx+@sp]
|
||
|
||
; The stack is word-aligned...
|
||
@sp = (@sp + ((@@type + 1) and (not 1)))
|
||
endm
|
||
ret_bx macro
|
||
ret (@sp - @bp)
|
||
endm
|
||
POPSTATE
|
||
|
||
; This allows `ret_bx` to be used in functions with no parameters as well,
|
||
; e.g. if parameters were removed between games. Simply don't pass any
|
||
; [params], and this macro will turn itself to a no-op, with `ret_bx` not
|
||
; clearing any parameters either.
|
||
if (@sp ne @bp)
|
||
mov bx, sp
|
||
endif
|
||
endm
|