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 5 commits
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
67 changes: 53 additions & 14 deletions man/rgbasm.5
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
.Nm rgbasm
.Nd language documentation
.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 .
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 .
.Pp
It is advisable to have some familiarity with the Game Boy hardware before reading this document.
Expand Down Expand Up @@ -47,6 +47,23 @@ Instructions are assembled into Game Boy opcodes.
Multiple instructions on one line can be separated by double colons
.Ql :: .
.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 identifiers (labels and other symbol names) are case-sensitive.
.Pp
Expand Down Expand Up @@ -324,9 +341,33 @@ 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.
.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
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,8 +386,11 @@ 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
Expand Down Expand Up @@ -513,8 +557,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,15 +596,10 @@ 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 .
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
.Ic IF ) ,
or 0 if only RGBLINK can compute its value.
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