Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement BITWIDTH and TZCOUNT functions #1450

Merged
merged 6 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/linkdefs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ enum RPNCommand {

RPN_HIGH = 0x70,
RPN_LOW = 0x71,
RPN_BITWIDTH = 0x72,
RPN_TZCOUNT = 0x73,

RPN_CONST = 0x80,
RPN_SYM = 0x81
Expand Down
60 changes: 45 additions & 15 deletions man/rgbasm.5
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,32 @@ with a zero constant as either operand will be constant 0, and
.Sq ||
with a non-zero constant as either operand will be constant 1, even if the other operand is non-constant.
.Pp
.Ic \&!
.Sq \&!
Rangi42 marked this conversation as resolved.
Show resolved Hide resolved
returns 1 if the operand was 0, and 0 otherwise.
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.
.EQ
delim $$
.EN
.Bl -column "BITWIDTH(n)"
.It Sy Name Ta Sy Operation
.It Fn BITWIDTH n Ta Returns the number of bits necessary to represent
.Ar n .
Some useful formulas:
.Ic BITWIDTH Ns Po Ar n Ns Pc - 1
equals $\[lf] roman log2 ( n ) \[rf]$,
.Ic BITWIDTH Ns Pq Ar n No - 1
equals $\[lc] roman log2 ( n ) \[rc]$,
and
.Ic No 32 - 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
Rangi42 marked this conversation as resolved.
Show resolved Hide resolved
.Ar n .
.El
.EQ
delim off
.EN
Rangi42 marked this conversation as resolved.
Show resolved Hide resolved
.Ss Fixed-point expressions
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.
Expand All @@ -345,18 +368,21 @@ Some integer operators like
and
.Sq -
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.
It follows that you can convert an integer to a fixed-point number by shifting it left.
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 that same amount.
ISSOtm marked this conversation as resolved.
Show resolved Hide resolved
.Pp
Note that the current number of fractional bits equals
Rangi42 marked this conversation as resolved.
Show resolved Hide resolved
.Ic TZCOUNT Ns Pq 1.0 .
.Pp
The following functions are designed to operate with fixed-point numbers:
.EQ
delim $$
.EN
.Bl -column -offset indent "ATAN2(y, x)"
.It Sy Name Ta Sy Operation
.It Fn DIV x y Ta Fixed-point division $( x \[di] y ) \[mu] ( 2 ^ precision )$
.It Fn MUL x y Ta Fixed-point multiplication $( x \[mu] y ) \[di] ( 2 ^ precision )$
.It Fn FMOD x y Ta Fixed-point modulo $( x % y ) \[di] ( 2 ^ precision )$
.It Fn DIV x y Ta Fixed-point division $( x ^\[di]^ y ) ^\[mu]^ ( 2 ^\[ha]^ precision )$
.It Fn MUL x y Ta Fixed-point multiplication $( x ^\[mu]^ y ) ^\[di]^ ( 2 ^\[ha]^ precision )$
.It Fn FMOD x y Ta Fixed-point modulo $( x ^%^ y ) ^\[di]^ ( 2 ^\[ha]^ precision )$
Rangi42 marked this conversation as resolved.
Show resolved Hide resolved
.It Fn POW x y Ta $x$ to the $y$ power
.It Fn LOG x y Ta Logarithm of $x$ to the base $y$
.It Fn ROUND x Ta Round $x$ to the nearest integer
Expand All @@ -383,11 +409,15 @@ no matter what value is set as the current
.Cm Q
option.
.Pp
The trigonometry functions (
.Ic SIN ,
.Ic COS ,
.Ic TAN ,
etc) are defined in terms of a circle divided into 1.0 "turns" (equal to 2pi radians or 360 degrees).
.EQ
delim $$
.EN
The trigonometry functions
.Pq Ic SIN , Ic COS , Ic TAN , No etc
are defined in terms of a circle divided into 1.0 "turns" (equal to $2 pi$ radians or 360 degrees).
.EQ
delim off
.EN
Rangi42 marked this conversation as resolved.
Show resolved Hide resolved
.Pp
These functions are useful for automatic generation of various tables.
For example:
Expand Down Expand Up @@ -513,8 +543,8 @@ There is also a character map stack that can be used to save and restore which c
.Sy Note :
Modifications to a character map take effect immediately from that point onward.
.Ss Other functions
There are a few other functions that do various useful things:
.Bl -column "DEF(symbol)"
There are a few other functions that do things beyond numeric or string operations:
.Bl -column "SECTION(symbol)"
.It Sy Name Ta Sy Operation
.It Fn BANK arg Ta Returns a bank number.
If
Expand Down Expand Up @@ -552,9 +582,9 @@ If
.Ar arg
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.
.It Fn DEF symbol Ta Returns TRUE (1) if
.It Fn DEF symbol Ta Returns 1 if
.Ar symbol
has been defined, FALSE (0) otherwise.
has been defined, 0 otherwise.
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 .
Expand Down
8 changes: 6 additions & 2 deletions man/rgbds.5
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,14 @@ The value is then ORed with $C7
.It Li $80 Ta Integer literal; followed by the
.Cm LONG
integer.
.It Li $70 Ta Ql HIGH
.It Li $70 Ta Cm HIGH
byte.
.It Li $71 Ta Ql LOW
.It Li $71 Ta Cm LOW
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
.Cm LONG
ID.
Expand Down
3 changes: 3 additions & 0 deletions src/asm/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> ke
{"LOW", T_(OP_LOW) },
{"ISCONST", T_(OP_ISCONST) },

{"BITWIDTH", T_(OP_BITWIDTH) },
{"TZCOUNT", T_(OP_TZCOUNT) },

{"STRCMP", T_(OP_STRCMP) },
{"STRIN", T_(OP_STRIN) },
{"STRRIN", T_(OP_STRRIN) },
Expand Down
8 changes: 8 additions & 0 deletions src/asm/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@
%type <int32_t> opt_q_arg

%token OP_HIGH "HIGH" OP_LOW "LOW"
%token OP_BITWIDTH "BITWIDTH"
%token OP_TZCOUNT "TZCOUNT"
%token OP_ISCONST "ISCONST"

%token OP_STRCMP "STRCMP"
Expand Down Expand Up @@ -1349,6 +1351,12 @@ relocexpr_no_str:
| OP_LOW LPAREN relocexpr RPAREN {
$$.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 {
$$.makeNumber($3.isKnown());
}
Expand Down
10 changes: 9 additions & 1 deletion src/asm/rpn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <string.h>
#include <string_view>

#include "helpers.hpp" // assume
#include "helpers.hpp" // assume, clz, ctz
#include "opmath.hpp"

#include "asm/output.hpp"
Expand Down Expand Up @@ -315,6 +315,12 @@ void Expression::makeUnaryOp(RPNCommand op, Expression &&src) {
case RPN_LOW:
data = val & 0xFF;
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_LOGAND:
Expand Down Expand Up @@ -494,6 +500,8 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
case RPN_RST:
case RPN_HIGH:
case RPN_LOW:
case RPN_BITWIDTH:
case RPN_TZCOUNT:
case RPN_CONST:
case RPN_SYM:
fatalerror("%d is not a binary operator\n", op);
Expand Down
11 changes: 10 additions & 1 deletion src/link/patch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <variant>
#include <vector>

#include "helpers.hpp" // assume
#include "helpers.hpp" // assume, clz, ctz
#include "linkdefs.hpp"
#include "opmath.hpp"

Expand Down Expand Up @@ -146,6 +146,15 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
value = popRPN(patch) & 0xFF;
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:
value = popRPN(patch) | popRPN(patch);
break;
Expand Down
11 changes: 11 additions & 0 deletions test/asm/bit-functions.asm
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions test/link/operators.asm
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ db @ <= 7
dw @ << 1
dw @ >> 1
dw @ >>> 1

db BITWIDTH(@)
db TZCOUNT(@)
Binary file modified test/link/operators.out.bin
Binary file not shown.
Loading