0

I'm trying to save a number 0xFD00 in a register (MIC-1 architecture). There are registers:

  • 0
  • +1
  • -1
  • AMASK: 0x0FFF
  • SMASK: 0x00FF

I can do left, right bit shifts and inverses. It is also possible to add values like SMASK + SMASK or AMASK + (-1). I can get 0xF000 with inverse(AMASK), but I'm not sure how to get 0xFD00 without too many steps.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
GeoCap
  • 505
  • 5
  • 15

3 Answers3

4

The most efficient sequence so far is @Falk Hüffner's answer, 3 operations. (Although it involves a + that isn't adding +1 or -1, or a value to itself).
This answer shows a way that takes 4 operations of the kinds explicitly mentioned in the question title.


0xFD is 0b11111101. So it has a bunch of contiguous bits, and one other stray set bit. One simple way would be

  • start with all-ones (-1, ~0, or 0 - 1)
  • left shift by 2 to get ...11111100
  • add 1 to get ...11111101 (0x...FD)
  • shift to the top of the register, shifting in low 0s and shifting out the high 1s, leaving the 0xFD at the top, with 8 low zeros.

I don't know MIC-1, but from your description it sounds like it can do those steps. If it can only shift by 1 position at a time, it will take 2 + 8 total shift instructions. There might be other ways to construct this constant more efficiently, maybe with something I'm not thinking of, or maybe with some capability the machine has.


Taking advantage of AMASK / SMASK and the way add / sub carry-propagation can flip a sequence of 1 / 0 bits respectively, along with Aki's observation that ~0xfd00 = 0x02ff, we can do the following:

initial AMASK = 0x00FF

AMASK += 1     (0x0100)
AMASK += AMASK (0x0200)  (left shift)
AMASK += SMASM (0x02FF)
NOT AMASK      (0xFD00)

See https://catonmat.net/low-level-bit-hacks for a nice intro to the kinds of shenanigans you can get up to with bitwise operations. (Although many of those also require AND, OR, or XOR. E.g. clearing the lowest set bit via x &= (x-1))


(Related: What are the best instruction sequences to generate vector constants on the fly? for x86 SIMD vectors: similar problem, you can generate -1 on the fly cheaply without loading from memory, and feed it through various other instructions like left shift, (SSSE3) absolute value. But only worth doing for short sequences, otherwise just load from memory or mov-immediate to integer registers and movd xmm0, eax)


Hex is just a way to represent numbers in text, e.g. ASCII. You could call it a serialization format.

0xFD00 is just another way to write 64768 (base 10) or 0b1111110100000000 (base 2). So you're just constructing a number in a register out of shifts and inc/dec. Assuming your bit-shifts multiply / divide by 2, not 10 or 16, those are binary operations so this is a binary number. It's just convenient to express the desired binary number in a compact format like hex, but at no point do you actually need hex, like a string of base-16 ASCII digits.

It's not a "hex number" when you construct it in a register, it's just a number. If anything it's binary.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
3

Assuming addition is possible with two registers and not just a constant, and left shift just drops the bits shifted out of the 16-bit word:

t = ~SMASK    // 0xff00
return t + (t << 1)

An alternative, if shifting is possible by arbitrary constants and not just 1, is

t = SMASK
t += -1
t += -1
t <<= 8
Falk Hüffner
  • 4,942
  • 19
  • 25
  • 1
    Oh yeah, good idea, left shift the high 7 bits by adding to themselves, opening up the gap that way. That gets it down to only 3 operations! This may be optimal; there may be other 3-step ways, but I doubt there are any 2-step ways. – Peter Cordes Jan 08 '22 at 19:37
  • 1
    The question is not clear on whether general addition is supported. The question's title seems to suggest it is not. Might want to add that as a caveat to the answer. – njuffa Jan 08 '22 at 20:21
  • Good point, I've added a note and an alternative (for which I'm also not sure it's legal). – Falk Hüffner Jan 08 '22 at 21:53
  • 1
    It's likely that most current and future readers of this Q&A will be interested in both your ideas; most people won't have a restriction against general addition, or would be interested in seeing what it can look like without that. – Peter Cordes Jan 08 '22 at 22:14
2

The inverse of 0xFD00 == 0b00000010 11111111 = 0x02ff

This can be achieved with the SMASK = 0x00FF * 3 + 2, or simply with SMASK | (1 << 9), if that's available.

a = smask
b = smask << 1
a = a + b
a++
a++
return ~a
Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57
  • Oh, I missed that we had `0x00FF` already available in registers. Hmm, `0xFF + 1` = `0x100` – Peter Cordes Jan 08 '22 at 12:35
  • 1
    Yeah, updated my answer with a 4-step way (vs. 5 for this, not counting amask or smask) that doesn't need a 2nd temporary location. Thanks for the inspiration to aim for `~0x02ff` starting with `0x00ff` – Peter Cordes Jan 08 '22 at 12:45