mirror of
https://github.com/ctm/syn68k.git
synced 2024-11-25 07:32:17 +00:00
216 lines
5.3 KiB
C
216 lines
5.3 KiB
C
#include "bitstring.h"
|
|
#include "error.h"
|
|
#include <string.h>
|
|
|
|
|
|
/* This takes a 16 bit number and a bitset (described by a lisp-style
|
|
* expression) and returns TRUE iff the number is a member of that set.
|
|
* A bitset would be described by an expression like:
|
|
*
|
|
* (union "00001111xx0011xx" "mmmm00111011xxx1"
|
|
* (intersect "1001xxxx0011nnn1" (not "0110001111000101")))
|
|
*
|
|
* The number will be in the set if the usual union, intersect and not
|
|
* properties are met. A number matches a simple string if each literal
|
|
* '0' and '1' in the string matches a 0 or 1 bit in the number, respectively.
|
|
* All non-'0' and non-'1' characters in the string are assumed to be
|
|
* wildcards; anything in the corresponding bit position in the number will
|
|
* be considered a match.
|
|
*/
|
|
|
|
BOOL
|
|
is_member_of_set (unsigned short n, List *set)
|
|
{
|
|
List *tmp;
|
|
char buf[512];
|
|
|
|
/* If we are looking for a match against a quoted string, efficiently
|
|
* determine if all of the literal bits match. This could be vastly
|
|
* faster if the quoted string were replaced with a mask of bits that must
|
|
* be valid and a set of values for those bits, but speed just doesn't
|
|
* matter here, and it's handy to deal with the original string (which
|
|
* can contain other information via the specific choice of non-'0'
|
|
* and non-'1' characters).
|
|
*/
|
|
if (set->token.type == TOK_QUOTED_STRING)
|
|
{
|
|
const char *s = set->token.u.string;
|
|
int mask;
|
|
|
|
|
|
for (mask = 1 << 15; mask != 0; s++, mask >>= 1)
|
|
if ((*s == '0' && (n & mask)) || (*s == '1' && !(n & mask)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (set->token.type != TOK_LIST)
|
|
{
|
|
/* This error will get printed a zillion times, but who cares. */
|
|
parse_error (set, "Malformed bit set; expecting quoted string of bits, "
|
|
"union, intersect, or not, found %s\n",
|
|
unparse_token (&set->token, buf));
|
|
return FALSE;
|
|
}
|
|
|
|
/* Not a member of the empty list. */
|
|
if (set->car == NULL)
|
|
return FALSE;
|
|
|
|
/* See what type of operation we are performing, and do the appropriate
|
|
* thing.
|
|
*/
|
|
switch (set->car->token.type) {
|
|
case TOK_UNION:
|
|
if (list_length (set->car) < 2)
|
|
parse_error (set->car, "Missing arguments to union.\n");
|
|
for (tmp = CDAR (set); tmp != NULL; tmp = tmp->cdr)
|
|
if (is_member_of_set (n, tmp))
|
|
return TRUE;
|
|
return FALSE;
|
|
case TOK_INTERSECT:
|
|
if (list_length (set->car) < 2)
|
|
parse_error (set->car, "Missing arguments to intersect.\n");
|
|
for (tmp = CDAR (set); tmp != NULL; tmp = tmp->cdr)
|
|
if (!is_member_of_set (n, tmp))
|
|
return FALSE;
|
|
return TRUE;
|
|
case TOK_NOT:
|
|
if (list_length (set->car) != 2)
|
|
{
|
|
parse_error (set->car, "Must have exactly one argument to not.\n");
|
|
return FALSE;
|
|
}
|
|
return !is_member_of_set (n, CDAR (set));
|
|
default:
|
|
/* This error will get printed a zillion times, but who cares. */
|
|
parse_error (set, "Malformed bit set; expecting quoted string of bits, "
|
|
"union, intersect, or not.\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
empty_set (List *set, int literal_bits_mask, int literal_bits)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 65536; i++)
|
|
if ((i & literal_bits_mask) == literal_bits && is_member_of_set (i, set))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Given a bit pattern like "0010ddd0000ddd001xxx001ppp" and a field number,
|
|
* returns by reference the low index and length of the nth field, where
|
|
* the first field (in this case the first string of 3 d's) is # 1. Returns
|
|
* TRUE if such a field exists, else FALSE.
|
|
*/
|
|
BOOL
|
|
pattern_range (const char *pattern, int which, PatternRange *range)
|
|
{
|
|
const char *p = pattern, *p2;
|
|
|
|
do
|
|
{
|
|
if (*p == '\0')
|
|
return FALSE;
|
|
|
|
/* Skip to first sequence. */
|
|
for (; *p != '\0' && (*p == '0' || *p == '1'); p++)
|
|
;
|
|
|
|
/* Remember where the pattern started. */
|
|
range->index = p - pattern;
|
|
p2 = p;
|
|
|
|
/* Find the end of the sequence. */
|
|
for (; *p != '\0' && *p == *p2; p++)
|
|
;
|
|
|
|
/* Remember the length of the pattern. */
|
|
range->length = p - p2;
|
|
}
|
|
while (--which > 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int
|
|
field_with_index (const char *opcode_bits, int index)
|
|
{
|
|
PatternRange range;
|
|
int i;
|
|
|
|
for (i = 1; pattern_range (opcode_bits, i, &range); i++)
|
|
{
|
|
if (range.index == index)
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
field_expanded (int field_number, const char *opcode_bits,
|
|
const char *bits_to_expand)
|
|
{
|
|
PatternRange range;
|
|
int i;
|
|
|
|
/* If there is no such field, return FALSE. */
|
|
if (!pattern_range (opcode_bits, field_number, &range))
|
|
return FALSE;
|
|
|
|
/* If the field falls outside the first 16 bits, it can't be expanded. */
|
|
if (range.index >= 16)
|
|
return FALSE;
|
|
|
|
for (i = range.index; i < range.index + range.length; i++)
|
|
if (bits_to_expand[i] == '-' && opcode_bits[i] != '0'
|
|
&& opcode_bits[i] != '1')
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int
|
|
num_fields (const char *opcode_bits)
|
|
{
|
|
PatternRange range;
|
|
int i;
|
|
|
|
for (i = 1; pattern_range (opcode_bits, i, &range); i++)
|
|
;
|
|
return i - 1;
|
|
}
|
|
|
|
|
|
void
|
|
make_unique_field_of_width (const char *opcode_bits, char *where, int width)
|
|
{
|
|
int i, x;
|
|
for (x = 'A'; strchr (opcode_bits, x) != NULL; x++)
|
|
;
|
|
for (i = 0; i < width; i++)
|
|
*where++ = x;
|
|
*where = '\0';
|
|
}
|
|
|
|
|
|
/* Handy utility function. */
|
|
void
|
|
print_16_bits (FILE *stream, unsigned short n)
|
|
{
|
|
int shift;
|
|
for (shift = 15; shift >= 0; shift--)
|
|
putc ('0' + ((n >> shift) & 1), stream);
|
|
}
|