Skip to content

15_Hex — the Hex Operations dialog, one page per operation

Level: 201 · for anyone with a hex editor open

One line: Every entry in 010 Editor's Hex Operations dialog is one line of C applied to each value in a range, and the line is the easy half: Treat Data As and Endian decide what a value is before the line runs, so the same operation on the same bytes writes different bytes when either one changes.

010 Editor opens the dialog from Tools > Hex Operations. On the left are twenty operations; on the right, a type, an operand, a description written in C, and an Options panel holding the range, the byte order and two advanced fields. This chapter gives the twenty a page each, and this page takes the controls once, so that the twenty can spend their words on what differs.

The reference throughout is the vendor's own page, Hex Operations, read against the copy of the manual bundled with 010 Editor 16.0.4 on 2026-09-12. It is documentation, not a measurement, and nothing on these pages has been run against the dialog itself. The programs are what CI checks.

The dialog, as a function

The manual describes each operation "in C notation, assuming that X[i] represents each value in the file to be modified". Every program in this chapter opens with the same function, hexop(), which is that sentence made runnable:

  1. cut the range into values of the type Treat Data As names — one, two, four or eight bytes — reading each in the order the Endian toggle names;
  2. apply the operation's formula to each value, with the operand;
  3. if an integer result does not fit its type, keep its low bits, and write it back where the value came from;
  4. add Operand Step to the operand, skip Skip Bytes bytes, and go on to the next value.

Step 3 is the one assumption the manual does not state. The rest is its sentence.

control what it decides
Treat Data As how many bytes make one value, and whether they are read as signed, unsigned or IEEE floating point
Operand, with Decimal and Hex the number the formula uses, and whether a 10 typed into the box is ten or sixteen — 0x10 and 10h are hex either way (Introduction to Number Systems)
Range the whole file, or only the selected bytes; with nothing selected, only Entire File is available
Endian which byte of a multi-byte value is the low one, both when the value is read and when the operand is laid out; it starts at the file's own setting, LIT or BIG in the status bar
Operand Step a number added to the operand after each value, so one operation writes a sequence
Skip Bytes bytes left untouched after each value, so one field of a fixed-size record changes and the rest do not

Verified output of the_dialog_py.py — regenerated by tools/run_examples.py, never hand-typed.

1. TREAT DATA AS DECIDES WHAT ONE VALUE IS
------------------------------------------------------------------------
   Four bytes, cut four ways. The file does not say which cut is
   meant; the dropdown and the Endian toggle do.

     the bytes                  01 00 02 00
     Unsigned Byte              X = 1, 0, 2, 0
     Unsigned Short, little     X = 1, 2
     Unsigned Short, big        X = 256, 512
     Unsigned Int, little       X = 131073

2. SO ONE OPERATION HAS MORE THAN ONE ANSWER
------------------------------------------------------------------------
   Add 1 to the first two of those bytes, 01 00, three ways:

     Unsigned Byte              02 01
     Unsigned Short, little     02 00
     Unsigned Short, big        01 01

   As bytes, each one gets its own 1. As one little-endian Short the
   1 lands on the first byte, and as a big-endian Short on the second.

3. SIGNED OR UNSIGNED CHANGES SOME FORMULAS AND NOT OTHERS
------------------------------------------------------------------------
   F0 is 240 as an Unsigned Byte and -16 as a Signed Byte:

     operation            Unsigned Byte   Signed Byte
     Add 1                f1              f1
     Multiply 2           e0              e0
     Divide 2             78              f8
     Set Minimum 16       f0              10

   Addition and multiplication keep the same low bits either way.
   Division and comparison have to know the number, and the number
   is what the dropdown changed.

4. OPERAND STEP ADDS TO THE OPERAND AFTER EACH VALUE
------------------------------------------------------------------------
   Assign 0 with Operand Step 1 counts up, one value at a time:

     Unsigned Byte,  8 bytes    00 01 02 03 04 05 06 07
     Unsigned Short, 8 bytes    00 00 01 00 02 00 03 00

5. SKIP BYTES LEAVES A GAP AFTER EACH VALUE
------------------------------------------------------------------------
   Two records, each a Signed Int id and then a four-byte name. Add 1
   with Skip Bytes 4 changes every id and no name:

     before  01 00 00 00 41 64 61 20 02 00 00 00 42 6f 62 20
     after   02 00 00 00 41 64 61 20 03 00 00 00 42 6f 62 20

     before  ids 1 and 2, names b'Ada ' and b'Bob '
     after   ids 2 and 3, names b'Ada ' and b'Bob '

6. RANGE: A SELECTION IS A SLICE
------------------------------------------------------------------------
   With Selection set, the bytes outside it are not values at all.
   Add 1 to six zero bytes, then to two of them selected at offset 2:

     Entire File   01 01 01 01 01 01
     Selection     00 00 01 01 00 00

Section 3 is the question worth asking of every operation, and the next program asks it of all twenty.

Which settings can change the result

The program below tries inputs rather than arguing: all 256 bytes for the sign, all 65,536 two-byte values for the byte order and the width, and a fixed sample of four-byte values for the byte order of an Int. For each setting it prints yes if it found one input on which the two choices write different bytes, and no if every input agreed.

Verified output of which_settings_matter_py.py — regenerated by tools/run_examples.py, never hand-typed.

1. WHICH SETTINGS CAN CHANGE THE BYTES AN OPERATION WRITES
------------------------------------------------------------------------
   sign: Signed against Unsigned Byte. endian: Big against Little,
   as a Short and as an Int. width: Short against Byte, same bytes.

     operation           sign   endian Short   endian Int   width
     Assign              no     yes            yes          yes
     Add                 no     yes            yes          yes
     Subtract            no     yes            yes          yes
     Multiply            no     yes            yes          yes
     Divide              yes    yes            yes          yes
     Negate              no     yes            yes          yes
     Modulus             yes    yes            yes          yes
     Set Minimum         yes    yes            yes          yes
     Set Maximum         yes    yes            yes          yes
     Swap Bytes          no     no             no           yes
     Binary And          no     yes            yes          yes
     Binary Or           no     yes            yes          yes
     Binary Xor          no     yes            yes          yes
     Binary Invert       no     no             no           no
     Shift Left          no     yes            yes          yes
     Shift Right         yes    yes            yes          yes
     Block Shift Left    no     yes            yes          yes
     Block Shift Right   ?      yes            yes          yes
     Rotate Left         no     no             yes          yes
     Rotate Right        no     no             yes          yes

2. THE FIRST INPUT ON WHICH SIGNED AND UNSIGNED DISAGREE
------------------------------------------------------------------------
   One byte, one operand, the byte each setting writes:

     Divide 2             on 80   Unsigned 40   Signed c0
     Modulus 3            on 80   Unsigned 02   Signed fe
     Set Minimum 0        on 80   Unsigned 80   Signed 00
     Set Maximum 0        on 80   Unsigned 00   Signed 80
     Shift Right 1        on 80   Unsigned 40   Signed c0

3. WHAT THE TABLE SAYS, COUNTED
------------------------------------------------------------------------
   no setting changes it:             Binary Invert
   the Endian toggle cannot change:   Swap Bytes, Binary Invert
   ...on a Short, but can on an Int:  Rotate Left, Rotate Right
   signed or unsigned is the same:    14 of 20

Three rows of that table are worth reading twice.

Binary Invert is the only operation no setting can change. Inverting a value inverts each of its bytes, whatever they are and whatever they mean, so there is nothing for the type, the sign or the byte order to decide. Every other operation depends on at least one of the three — even Assign, which reads nothing, depends on two.

Only five are known to care about the sign. Divide, Modulus, Set Minimum, Set Maximum and Shift Right have to know the number a value holds; the other fourteen that were measured only move bits, and two's complement makes a signed value and an unsigned value the same bits. Arithmetic has its own width is the same fact from the other side. Shift Right's yes assumes the sign bit is copied in, which C leaves to the implementation, and Block Shift Right's ? is the same question without even that.

A rotation ignores the Endian toggle on a Short, and not on an Int. Reading a Short in the other order swaps its two bytes, which is itself a rotation by eight bits, and two rotations of the same width give the same result in either order. Reversing four bytes is not a rotation of 32 bits. Rotate Right works it through.

The twenty

In the dialog's own order: seven arithmetic operations, two limits, a byte swap, four masks, and six that move bits.

# Lesson The question it answers Status
1 Assign How does one operand fill a range, and why does the same 41 write four different patterns? written, 2026-09-12
2 Add I added to every value — why did the carry reach one byte and not the next? written, 2026-09-12
3 Subtract What is zero minus one in a byte, and why is Subtract a special case of Add? written, 2026-09-12
4 Multiply What is kept when a product does not fit, and why does Multiply 171 undo Multiply 3? written, 2026-09-12
5 Divide Why does F0 divided by 2 give 78 or F8 depending on one dropdown? written, 2026-09-12
6 Negate What is minus a byte, and which values are their own negative? written, 2026-09-12
7 Modulus Whose sign does a remainder take, and when is Modulus 16 the low hex digit? written, 2026-09-12
8 Set Minimum Which operation turns control characters into spaces, and why does it eat UTF-8 as a Signed Byte? written, 2026-09-12
9 Set Maximum Why does a ceiling of 7F catch every UTF-8 byte, or none of them? written, 2026-09-12
10 Swap Bytes How do I turn UTF-16LE into UTF-16BE in place, and why can the Endian toggle not change the result? written, 2026-09-12
11 Binary And How does a mask clear bits, and why does And DF capitalise café and misspell Łódź? written, 2026-09-12
12 Binary Or Why does Or 20 lowercase ASCII and break every two-byte UTF-8 character? written, 2026-09-12
13 Binary Xor Why does Xor twice give the file back, and why is a one-byte key no secret? written, 2026-09-12
14 Binary Invert Why is Invert the one operation no setting can change? written, 2026-09-12
15 Shift Left Where do the bits go, and why do they cross rightward on a little-endian Short? written, 2026-09-12
16 Shift Right What comes in at the top: a zero, or a copy of the sign? written, 2026-09-12
17 Block Shift Left What if the bits spill into the neighbouring value instead of falling off? written, 2026-09-12
18 Block Shift Right How does a block shift insert a byte without changing the size of the range? written, 2026-09-12
19 Rotate Left What if the bits that fall off come back, and why is Rotate Left 4 a swap of hex digits? written, 2026-09-12
20 Rotate Right How can a rotation turn +1 into −128, and why does the Endian toggle leave a Short's rotation alone? written, 2026-09-12

The same twenty in Rust

The manual never says what a result that does not fit should become. Rust will not let that question go unasked: the same line has to be written wrapping_, checked_ or saturating_, and the method's name is the answer. The first half below is the operations as one line each on a u8; the second half is the manual's open questions, answered each of the ways Rust offers.

Verified output of hex_operations_rs.rs — regenerated by tools/run_examples.py, never hand-typed.

1. THE TWENTY, ON ONE BYTE
------------------------------------------------------------------------
   x is 0xf0u8, which is 240; s is the same bits as an i8, -16.
   Treat Data As is the type, and the dialog's formula is the line.

     Assign 0x41              0x41                     41
     Add 0x20                 x.wrapping_add(0x20)     10
     Subtract 0xf1            x.wrapping_sub(0xf1)     ff
     Multiply 3               x.wrapping_mul(3)        d0
     Divide 3                 x / 3                    50
     Divide 3, Signed         s / 3                    fb
     Negate                   x.wrapping_neg()         10
     Modulus 7                x % 7                    02
     Modulus 7, Signed        s % 7                    fe
     Set Minimum 0xf8         x.max(0xf8)              f8
     Set Maximum 0x7f         x.min(0x7f)              7f
     Binary And 0x3c          x & 0x3c                 30
     Binary Or 0x0f           x | 0x0f                 ff
     Binary Xor 0xff          x ^ 0xff                 0f
     Binary Invert            !x                       0f
     Shift Left 1             x << 1                   e0
     Shift Right 1            x >> 1                   78
     Shift Right 1, Signed    s >> 1                   f8
     Rotate Left 4            x.rotate_left(4)         0f
     Rotate Right 1           x.rotate_right(1)        78

   The two that do not fit in one byte:

     Swap Bytes, Short        v.swap_bytes()           12 34
     Block Shift Left 4       v << 4, as one u32       23 45 67 80
     Block Shift Right 4      v >> 4, as one u32       01 23 45 67
     (Swap Bytes starts from 34 12; the Block Shifts from 12 34 56 78.)

2. WHAT THE C NOTATION DOES NOT SAY, RUST MAKES YOU SAY
------------------------------------------------------------------------
   Each group is one question the manual leaves open, and each line one
   answer Rust will let you choose.

     0xf0u8.wrapping_add(0x20)      0x10         a result that does not fit
     0xf0u8.checked_add(0x20)       None
     0xf0u8.saturating_add(0x20)    0xff

     i8::MIN.wrapping_neg()         -128         the value with no positive twin
     i8::MIN.checked_neg()          None

     0x10u8.checked_div(0)          None         dividing by zero

     -7i8 / 2                       -3           a negative quotient
     (-7i8).div_euclid(2)           -4
     -7i8 % 3                       -1           a negative remainder
     (-7i8).rem_euclid(3)           2

     0x81u8.wrapping_shl(9)         0x02         a shift of the width or more
     0x81u8.checked_shl(9)          None
     0x81u8.rotate_left(9)          0x03

     0x1234u16.rotate_left(8)       0x3412       a rotation that is a byte swap
     0x1234u16.swap_bytes()         0x3412

What the manual does not say

Everything on these pages that depends on one of these is labelled where it appears, and none of it is claimed for the dialog:

  • what an integer result that does not fit its type becomes — the programs keep the low bits, C's rule for unsigned arithmetic;
  • what Divide and Modulus do with an operand of 0;
  • what Shift Right and Block Shift Right bring in at the top of a negative signed value;
  • what a shift or a rotation by the width of the type, or more, does;
  • how Block Shift lays values wider than a byte end to end on a little-endian file;
  • what is done with bytes at the end of a range too few to make a whole value;
  • which operations accept Float and Double — the manual says only that "some operations can only be used on certain data types".

Each one is a question for the dialog itself, on a copy of a file: type a few bytes you have written down, run the operation, and compare what it wrote with what the page predicts.

A note on the code

Every page has one Python program, and each program opens with the same hexop() and the same TREAT_AS table, copied rather than imported so that each runs on its own. The chapter page adds the Rust above, because a Rust integer type is Treat Data As with the overflow policy written into the method name. Nothing here needs a hex editor to run, and nothing here is evidence about one.

See also