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:
- 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;
- apply the operation's formula to each value, with the operand;
- if an integer result does not fit its type, keep its low bits, and write it back where the value came from;
- 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¶
- The bytes do not say which end — 010 Editor's per-file
LITandBIGsetting, which the Endian toggle starts from - Which base did you mean? — the operand box's Decimal and Hex toggle, and the fields that do not say
- Arithmetic has its own width — why the width of a value decides what a carry does
- Bytes, hex and int — the Python conversions
hexop()is built from