mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Implement BITWIDTH and TZCOUNT functions (#1450)
This commit is contained in:
@@ -55,6 +55,8 @@ enum RPNCommand {
|
|||||||
|
|
||||||
RPN_HIGH = 0x70,
|
RPN_HIGH = 0x70,
|
||||||
RPN_LOW = 0x71,
|
RPN_LOW = 0x71,
|
||||||
|
RPN_BITWIDTH = 0x72,
|
||||||
|
RPN_TZCOUNT = 0x73,
|
||||||
|
|
||||||
RPN_CONST = 0x80,
|
RPN_CONST = 0x80,
|
||||||
RPN_SYM = 0x81
|
RPN_SYM = 0x81
|
||||||
|
|||||||
67
man/rgbasm.5
67
man/rgbasm.5
@@ -9,9 +9,9 @@
|
|||||||
.Nm rgbasm
|
.Nm rgbasm
|
||||||
.Nd language documentation
|
.Nd language documentation
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
This is the full description of the language used by
|
This is the full description of the assembly language used by
|
||||||
.Xr rgbasm 1 .
|
.Xr rgbasm 1 .
|
||||||
The description of the instructions supported by the Game Boy CPU is in
|
For the full description of instructions in the machine language supported by the Game Boy CPU, see
|
||||||
.Xr gbz80 7 .
|
.Xr gbz80 7 .
|
||||||
.Pp
|
.Pp
|
||||||
It is advisable to have some familiarity with the Game Boy hardware before reading this document.
|
It is advisable to have some familiarity with the Game Boy hardware before reading this document.
|
||||||
@@ -47,6 +47,23 @@ Instructions are assembled into Game Boy opcodes.
|
|||||||
Multiple instructions on one line can be separated by double colons
|
Multiple instructions on one line can be separated by double colons
|
||||||
.Ql :: .
|
.Ql :: .
|
||||||
.Pp
|
.Pp
|
||||||
|
The available instructions are documented in
|
||||||
|
.Xr gbz80 7 .
|
||||||
|
Note that where an instruction requires an 8-bit register
|
||||||
|
.Ar r8 ,
|
||||||
|
.Nm
|
||||||
|
can interpret
|
||||||
|
.Ic HIGH Ns Pq Ar r16
|
||||||
|
as the top 8-bit register of the given
|
||||||
|
.Ar r16 ,
|
||||||
|
and
|
||||||
|
.Ic LOW Ns Pq Ar r16
|
||||||
|
as the bottom one (except for
|
||||||
|
.Ic LOW Ns Pq Ic AF ,
|
||||||
|
since
|
||||||
|
.Ic F
|
||||||
|
is not a valid register).
|
||||||
|
.Pp
|
||||||
All reserved keywords (directives, register names, etc.) are case-insensitive;
|
All reserved keywords (directives, register names, etc.) are case-insensitive;
|
||||||
all identifiers (labels and other symbol names) are case-sensitive.
|
all identifiers (labels and other symbol names) are case-sensitive.
|
||||||
.Pp
|
.Pp
|
||||||
@@ -324,9 +341,33 @@ with a zero constant as either operand will be constant 0, and
|
|||||||
.Sq ||
|
.Sq ||
|
||||||
with a non-zero constant as either operand will be constant 1, even if the other operand is non-constant.
|
with a non-zero constant as either operand will be constant 1, even if the other operand is non-constant.
|
||||||
.Pp
|
.Pp
|
||||||
.Ic \&!
|
.Sq \&!
|
||||||
returns 1 if the operand was 0, and 0 otherwise.
|
returns 1 if the operand was 0, and 0 otherwise.
|
||||||
Even a non-constant operand with any non-zero bits will return 0.
|
Even a non-constant operand with any non-zero bits will return 0.
|
||||||
|
.Ss Integer functions
|
||||||
|
Besides operators, there are also some functions which have more specialized uses.
|
||||||
|
.Bl -column "BITWIDTH(n)"
|
||||||
|
.It Sy Name Ta Sy Operation
|
||||||
|
.It Fn HIGH n Ta Equivalent to Ql Ar n No & $FF .
|
||||||
|
.It Fn LOW n Ta Equivalent to Ql Po Ns Ar n No & $FF00 Pc >> 8 .
|
||||||
|
.EQ
|
||||||
|
delim $$
|
||||||
|
.EN
|
||||||
|
.It Fn BITWIDTH n Ta Returns the number of bits necessary to represent
|
||||||
|
.Ar n .
|
||||||
|
Some useful formulas:
|
||||||
|
.Ic BITWIDTH Ns ( Ar n Ns )\ \-\ 1
|
||||||
|
equals $\[lf] log sub 2 ( n ) \[rf]$,
|
||||||
|
.Ic BITWIDTH Ns Pq Ar n Ns \ \-\ 1
|
||||||
|
equals $\[lc] log sub 2 ( n ) \[rc]$, and
|
||||||
|
.No 32\ \-\ Ns Ic BITWIDTH Ns Pq Ar n
|
||||||
|
equals $roman clz ( n )$.
|
||||||
|
.It Fn TZCOUNT n Ta Returns $roman ctz ( n )$, the count of trailing zero bits at the end of the binary representation of
|
||||||
|
.Ar n .
|
||||||
|
.El
|
||||||
|
.EQ
|
||||||
|
delim off
|
||||||
|
.EN
|
||||||
.Ss Fixed-point expressions
|
.Ss Fixed-point expressions
|
||||||
Fixed-point numbers are basically normal (32-bit) integers, which count fractions instead of whole numbers.
|
Fixed-point numbers are basically normal (32-bit) integers, which count fractions instead of whole numbers.
|
||||||
They offer better precision than integers but limit the range of values.
|
They offer better precision than integers but limit the range of values.
|
||||||
@@ -345,8 +386,11 @@ Some integer operators like
|
|||||||
and
|
and
|
||||||
.Sq -
|
.Sq -
|
||||||
don't care whether the operands are integers or fixed-point.
|
don't care whether the operands are integers or fixed-point.
|
||||||
You can easily truncate a fixed-point number into an integer by shifting it right by 16 bits.
|
You can easily truncate a fixed-point number into an integer by shifting it right by the number of fractional bits.
|
||||||
It follows that you can convert an integer to a fixed-point number by shifting it left.
|
It follows that you can convert an integer to a fixed-point number by shifting it left that same amount.
|
||||||
|
.Pp
|
||||||
|
Note that the current number of fractional bits can be computed as
|
||||||
|
.Ic TZCOUNT Ns Pq 1.0 .
|
||||||
.Pp
|
.Pp
|
||||||
The following functions are designed to operate with fixed-point numbers:
|
The following functions are designed to operate with fixed-point numbers:
|
||||||
.EQ
|
.EQ
|
||||||
@@ -513,8 +557,8 @@ There is also a character map stack that can be used to save and restore which c
|
|||||||
.Sy Note :
|
.Sy Note :
|
||||||
Modifications to a character map take effect immediately from that point onward.
|
Modifications to a character map take effect immediately from that point onward.
|
||||||
.Ss Other functions
|
.Ss Other functions
|
||||||
There are a few other functions that do various useful things:
|
There are a few other functions that do things beyond numeric or string operations:
|
||||||
.Bl -column "DEF(symbol)"
|
.Bl -column "SECTION(symbol)"
|
||||||
.It Sy Name Ta Sy Operation
|
.It Sy Name Ta Sy Operation
|
||||||
.It Fn BANK arg Ta Returns a bank number.
|
.It Fn BANK arg Ta Returns a bank number.
|
||||||
If
|
If
|
||||||
@@ -552,15 +596,10 @@ If
|
|||||||
.Ar arg
|
.Ar arg
|
||||||
is a section type keyword, it returns the starting address of that section type.
|
is a section type keyword, it returns the starting address of that section type.
|
||||||
The result is not constant, since only RGBLINK can compute its value.
|
The result is not constant, since only RGBLINK can compute its value.
|
||||||
.It Fn DEF symbol Ta Returns TRUE (1) if
|
.It Fn DEF symbol Ta Returns 1 if
|
||||||
.Ar symbol
|
.Ar symbol
|
||||||
has been defined, FALSE (0) otherwise.
|
has been defined, 0 otherwise.
|
||||||
String constants are not expanded within the parentheses.
|
String constants are not expanded within the parentheses.
|
||||||
.It Fn HIGH arg Ta Returns the top 8 bits of the operand if Ar arg No is a label or constant, or the top 8-bit register if it is a 16-bit register .
|
|
||||||
.It Fn LOW arg Ta Returns the bottom 8 bits of the operand if Ar arg No is a label or constant, or the bottom 8-bit register if it is a 16-bit register Pq Cm AF No isn't a valid register for this function .
|
|
||||||
The result may be constant if
|
|
||||||
.Nm
|
|
||||||
is able to compute it.
|
|
||||||
.It Fn ISCONST arg Ta Returns 1 if Ar arg Ap s value is known by RGBASM (e.g. if it can be an argument to
|
.It Fn ISCONST arg Ta Returns 1 if Ar arg Ap s value is known by RGBASM (e.g. if it can be an argument to
|
||||||
.Ic IF ) ,
|
.Ic IF ) ,
|
||||||
or 0 if only RGBLINK can compute its value.
|
or 0 if only RGBLINK can compute its value.
|
||||||
|
|||||||
@@ -391,10 +391,14 @@ The value is then ORed with $C7
|
|||||||
.It Li $80 Ta Integer literal; followed by the
|
.It Li $80 Ta Integer literal; followed by the
|
||||||
.Cm LONG
|
.Cm LONG
|
||||||
integer.
|
integer.
|
||||||
.It Li $70 Ta Ql HIGH
|
.It Li $70 Ta Cm HIGH
|
||||||
byte.
|
byte.
|
||||||
.It Li $71 Ta Ql LOW
|
.It Li $71 Ta Cm LOW
|
||||||
byte.
|
byte.
|
||||||
|
.It Li $72 Ta Cm BITWIDTH
|
||||||
|
value.
|
||||||
|
.It Li $73 Ta Cm TZCOUNT
|
||||||
|
value.
|
||||||
.It Li $81 Ta A symbol's value; followed by the symbol's
|
.It Li $81 Ta A symbol's value; followed by the symbol's
|
||||||
.Cm LONG
|
.Cm LONG
|
||||||
ID.
|
ID.
|
||||||
|
|||||||
@@ -237,6 +237,9 @@ static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> ke
|
|||||||
{"LOW", T_(OP_LOW) },
|
{"LOW", T_(OP_LOW) },
|
||||||
{"ISCONST", T_(OP_ISCONST) },
|
{"ISCONST", T_(OP_ISCONST) },
|
||||||
|
|
||||||
|
{"BITWIDTH", T_(OP_BITWIDTH) },
|
||||||
|
{"TZCOUNT", T_(OP_TZCOUNT) },
|
||||||
|
|
||||||
{"STRCMP", T_(OP_STRCMP) },
|
{"STRCMP", T_(OP_STRCMP) },
|
||||||
{"STRIN", T_(OP_STRIN) },
|
{"STRIN", T_(OP_STRIN) },
|
||||||
{"STRRIN", T_(OP_STRRIN) },
|
{"STRRIN", T_(OP_STRRIN) },
|
||||||
|
|||||||
@@ -174,6 +174,8 @@
|
|||||||
%type <int32_t> opt_q_arg
|
%type <int32_t> opt_q_arg
|
||||||
|
|
||||||
%token OP_HIGH "HIGH" OP_LOW "LOW"
|
%token OP_HIGH "HIGH" OP_LOW "LOW"
|
||||||
|
%token OP_BITWIDTH "BITWIDTH"
|
||||||
|
%token OP_TZCOUNT "TZCOUNT"
|
||||||
%token OP_ISCONST "ISCONST"
|
%token OP_ISCONST "ISCONST"
|
||||||
|
|
||||||
%token OP_STRCMP "STRCMP"
|
%token OP_STRCMP "STRCMP"
|
||||||
@@ -1349,6 +1351,12 @@ relocexpr_no_str:
|
|||||||
| OP_LOW LPAREN relocexpr RPAREN {
|
| OP_LOW LPAREN relocexpr RPAREN {
|
||||||
$$.makeUnaryOp(RPN_LOW, std::move($3));
|
$$.makeUnaryOp(RPN_LOW, std::move($3));
|
||||||
}
|
}
|
||||||
|
| OP_BITWIDTH LPAREN relocexpr RPAREN {
|
||||||
|
$$.makeUnaryOp(RPN_BITWIDTH, std::move($3));
|
||||||
|
}
|
||||||
|
| OP_TZCOUNT LPAREN relocexpr RPAREN {
|
||||||
|
$$.makeUnaryOp(RPN_TZCOUNT, std::move($3));
|
||||||
|
}
|
||||||
| OP_ISCONST LPAREN relocexpr RPAREN {
|
| OP_ISCONST LPAREN relocexpr RPAREN {
|
||||||
$$.makeNumber($3.isKnown());
|
$$.makeNumber($3.isKnown());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include "helpers.hpp" // assume
|
#include "helpers.hpp" // assume, clz, ctz
|
||||||
#include "opmath.hpp"
|
#include "opmath.hpp"
|
||||||
|
|
||||||
#include "asm/output.hpp"
|
#include "asm/output.hpp"
|
||||||
@@ -319,6 +319,12 @@ void Expression::makeUnaryOp(RPNCommand op, Expression &&src) {
|
|||||||
case RPN_LOW:
|
case RPN_LOW:
|
||||||
data = val & 0xFF;
|
data = val & 0xFF;
|
||||||
break;
|
break;
|
||||||
|
case RPN_BITWIDTH:
|
||||||
|
data = val != 0 ? 32 - clz((uint32_t)val) : 0;
|
||||||
|
break;
|
||||||
|
case RPN_TZCOUNT:
|
||||||
|
data = val != 0 ? ctz((uint32_t)val) : 32;
|
||||||
|
break;
|
||||||
|
|
||||||
case RPN_LOGOR:
|
case RPN_LOGOR:
|
||||||
case RPN_LOGAND:
|
case RPN_LOGAND:
|
||||||
@@ -498,6 +504,8 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
case RPN_RST:
|
case RPN_RST:
|
||||||
case RPN_HIGH:
|
case RPN_HIGH:
|
||||||
case RPN_LOW:
|
case RPN_LOW:
|
||||||
|
case RPN_BITWIDTH:
|
||||||
|
case RPN_TZCOUNT:
|
||||||
case RPN_CONST:
|
case RPN_CONST:
|
||||||
case RPN_SYM:
|
case RPN_SYM:
|
||||||
fatalerror("%d is not a binary operator\n", op);
|
fatalerror("%d is not a binary operator\n", op);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "helpers.hpp" // assume
|
#include "helpers.hpp" // assume, clz, ctz
|
||||||
#include "linkdefs.hpp"
|
#include "linkdefs.hpp"
|
||||||
#include "opmath.hpp"
|
#include "opmath.hpp"
|
||||||
|
|
||||||
@@ -146,6 +146,15 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
value = popRPN(patch) & 0xFF;
|
value = popRPN(patch) & 0xFF;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RPN_BITWIDTH:
|
||||||
|
value = popRPN(patch);
|
||||||
|
value = value != 0 ? 32 - clz((uint32_t)value) : 0;
|
||||||
|
break;
|
||||||
|
case RPN_TZCOUNT:
|
||||||
|
value = popRPN(patch);
|
||||||
|
value = value != 0 ? ctz((uint32_t)value) : 32;
|
||||||
|
break;
|
||||||
|
|
||||||
case RPN_OR:
|
case RPN_OR:
|
||||||
value = popRPN(patch) | popRPN(patch);
|
value = popRPN(patch) | popRPN(patch);
|
||||||
break;
|
break;
|
||||||
|
|||||||
11
test/asm/bit-functions.asm
Normal file
11
test/asm/bit-functions.asm
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
assert BITWIDTH(0) == 0
|
||||||
|
assert BITWIDTH(42) == 6
|
||||||
|
assert BITWIDTH(-1) == 32
|
||||||
|
assert BITWIDTH($80000000) == 32
|
||||||
|
|
||||||
|
assert TZCOUNT(0) == 32
|
||||||
|
assert TZCOUNT(42) == 1
|
||||||
|
assert TZCOUNT(-1) == 0
|
||||||
|
assert TZCOUNT($80000000) == 31
|
||||||
|
|
||||||
|
assert TZCOUNT(1.0) == 16
|
||||||
@@ -27,3 +27,6 @@ db @ <= 7
|
|||||||
dw @ << 1
|
dw @ << 1
|
||||||
dw @ >> 1
|
dw @ >> 1
|
||||||
dw @ >>> 1
|
dw @ >>> 1
|
||||||
|
|
||||||
|
db BITWIDTH(@)
|
||||||
|
db TZCOUNT(@)
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user