One week ago I finally had to confront
a long-standing defect in lDebug's
expression evaluator: It didn't support
types. I found out because all numbers
that were "negative" in two's complement
(ie values that are >= 8000_0000h
unsigned)
were accepted for narrower callers,
that is, getword
or getbyte
.
These functions are called for things
such as offsets, E/S/F command strings,
and permanent breakpoint counters.
I didn't notice for a long time
because I only used fitting values
or -1, the latter which happened to
be allowed through because of a bug.
The bug was that the getexpression
counting of significant bits
ended up with a count of 32 bits for
large numbers, yet the protocol defined
the least five bits
as holding the count.
Therefore a count of 32 overflowed to 0 and
the callers that did check the count
(only getword and getbyte
at the time) never detected that these
large numbers were outside their limits.
Now the obvious fix was to use six bits to return the count. But that broke the acceptance of negative values such as -1. So the next step was to treat all numbers as signed. As marked in the changeset description we considered this a magic hack though.
The proper fix
came along briefly after the magic:
Almost complete support for types.
In particular, the unary minus operator
sets its value's type to negative,
allowing literals prefixed by a
unary minus to be handled as signed
numbers by the significant bits counting.
Explicit type keywords such as sword
(signed word) versus word
(unsigned word)
also allow to specify signedness.
Other than that, binary operators
have a complicated relationship to types.
As noted in the changeset message
we may revisit some of those later on.
Eventually one test
had to be dropped because it passed
a literal of FFFF_FFFF
for a
permanent breakpoint's counter.
(Literals without any type keyword or
operator are considered unsigned
by default, like most all expression terms,
except for parens-surrounded subexpressions.)
This is now considered broken and
unsupported. Note that minus one is
still allowed, as desired.
The type support soon was used to
implement a feature
we'd long desired:
specifying a pointer
type
allows to use a 32-bit expression
as a 16:16 far pointer.
This is most useful with compound
variables like csip
and sssp
or with a memory dword that we want to
read as a far pointer without specifying
the memory address twice, eg g pointer [ss:sp]
to return from a 16-bit far function
or interrupt handler. (This previously
required a command like
g word [ss:sp + 2]:word [ss:sp]
.)
Besides, getaddr
now also re-uses
the check for 16 significant bits.
Another change that came along
was checking the significant bit widths
for R commands
that write to a variable or memory location.
The need for this check had been noted
in a comment
that originated in an early revision, in 2010 October.
This had the knock-on effect of failing a
test that used a command
reading r ax ^:= -1
to bitwise invert a 16-bit register.
We fixed this
by inserting a word
type keyword.
Writing of old revisions, the lack of types in the expression evaluator goes back all the way to the initial commit of lDebug in 2010 (still called NDebug at the time).